Feature/#574 look at httpclient cache key (#589)

* #574 consolidate some code, man the config stuff is a mess!

* #574 just use the downstream re route and the key for caching http clients

* #574 added benchmark, i was suprised to learn using a complex type was faster than a string in benchmark .net dictionary tests, hey ho probably dont have enough data in the type...
This commit is contained in:
Tom Pallister
2018-09-01 13:10:45 +01:00
committed by GitHub
parent 55277cac45
commit 66b68fc685
40 changed files with 672 additions and 289 deletions

View File

@ -50,7 +50,7 @@
Logger.LogWarning("user scopes is not authorised setting pipeline error");
SetPipelineError(context, new UnauthorisedError(
$"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}"));
$"{context.HttpContext.User.Identity.Name} unable to access {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}"));
}
}
@ -70,19 +70,19 @@
if (IsAuthorised(authorised))
{
Logger.LogInformation($"{context.HttpContext.User.Identity.Name} has succesfully been authorised for {context.DownstreamReRoute.UpstreamPathTemplate.Value}.");
Logger.LogInformation($"{context.HttpContext.User.Identity.Name} has succesfully been authorised for {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}.");
await _next.Invoke(context);
}
else
{
Logger.LogWarning($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}. Setting pipeline error");
Logger.LogWarning($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error");
SetPipelineError(context, new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.Value}"));
SetPipelineError(context, new UnauthorisedError($"{context.HttpContext.User.Identity.Name} is not authorised to access {context.DownstreamReRoute.UpstreamPathTemplate.OriginalValue}"));
}
}
else
{
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value} route does not require user to be authorised");
await _next.Invoke(context);
}
}

View File

@ -11,7 +11,6 @@ namespace Ocelot.Configuration.Builder
private AuthenticationOptions _authenticationOptions;
private string _loadBalancerKey;
private string _downstreamPathTemplate;
private string _upstreamTemplate;
private UpstreamPathTemplate _upstreamTemplatePattern;
private List<HttpMethod> _upstreamHttpMethod;
private bool _isAuthenticated;
@ -79,12 +78,6 @@ namespace Ocelot.Configuration.Builder
return this;
}
public DownstreamReRouteBuilder WithUpstreamPathTemplate(string input)
{
_upstreamTemplate = input;
return this;
}
public DownstreamReRouteBuilder WithUpstreamTemplatePattern(UpstreamPathTemplate input)
{
_upstreamTemplatePattern = input;
@ -245,7 +238,7 @@ namespace Ocelot.Configuration.Builder
{
return new DownstreamReRoute(
_key,
new PathTemplate(_upstreamTemplate),
_upstreamTemplatePattern,
_upstreamHeaderFindAndReplace,
_downstreamHeaderFindAndReplace,
_downstreamAddresses,
@ -267,7 +260,7 @@ namespace Ocelot.Configuration.Builder
_isAuthenticated,
_isAuthorised,
_authenticationOptions,
new PathTemplate(_downstreamPathTemplate),
new DownstreamPathTemplate(_downstreamPathTemplate),
_loadBalancerKey,
_delegatingHandlers,
_addHeadersToDownstream,

View File

@ -1,15 +1,12 @@
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Values;
using System.Linq;
using Ocelot.Configuration.Creator;
using System;
namespace Ocelot.Configuration.Builder
namespace Ocelot.Configuration.Builder
{
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Values;
using System.Linq;
public class ReRouteBuilder
{
private string _upstreamTemplate;
private UpstreamPathTemplate _upstreamTemplatePattern;
private List<HttpMethod> _upstreamHttpMethod;
private string _upstreamHost;
@ -39,12 +36,6 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ReRouteBuilder WithUpstreamPathTemplate(string input)
{
_upstreamTemplate = input;
return this;
}
public ReRouteBuilder WithUpstreamTemplatePattern(UpstreamPathTemplate input)
{
_upstreamTemplatePattern = input;
@ -67,7 +58,6 @@ namespace Ocelot.Configuration.Builder
{
return new ReRoute(
_downstreamReRoutes,
new PathTemplate(_upstreamTemplate),
_upstreamHttpMethod,
_upstreamTemplatePattern,
_upstreamHost,

View File

@ -0,0 +1,41 @@
namespace Ocelot.Configuration.Builder
{
using Values;
public class UpstreamPathTemplateBuilder
{
private string _template;
private int _priority;
private bool _containsQueryString;
private string _originalValue;
public UpstreamPathTemplateBuilder WithTemplate(string template)
{
_template = template;
return this;
}
public UpstreamPathTemplateBuilder WithPriority(int priority)
{
_priority = priority;
return this;
}
public UpstreamPathTemplateBuilder WithContainsQueryString(bool containsQueryString)
{
_containsQueryString = containsQueryString;
return this;
}
public UpstreamPathTemplateBuilder WithOriginalValue(string originalValue)
{
_originalValue = originalValue;
return this;
}
public UpstreamPathTemplate Build()
{
return new UpstreamPathTemplate(_template, _priority, _containsQueryString, _originalValue);
}
}
}

View File

@ -166,7 +166,6 @@ namespace Ocelot.Configuration.Creator
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(aggregateReRoute);
var reRoute = new ReRouteBuilder()
.WithUpstreamPathTemplate(aggregateReRoute.UpstreamPathTemplate)
.WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod)
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
.WithDownstreamReRoutes(applicableReRoutes)
@ -182,7 +181,6 @@ namespace Ocelot.Configuration.Creator
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
var reRoute = new ReRouteBuilder()
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
.WithDownstreamReRoute(downstreamReRoutes)
@ -229,7 +227,6 @@ namespace Ocelot.Configuration.Creator
var reRoute = new DownstreamReRouteBuilder()
.WithKey(fileReRoute.Key)
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
.WithIsAuthenticated(fileReRouteOptions.IsAuthenticated)

View File

@ -31,7 +31,7 @@ namespace Ocelot.Configuration.Creator
//hack to handle /{url} case
if(ForwardSlashAndOnePlaceHolder(upstreamTemplate, placeholders, postitionOfPlaceHolderClosingBracket))
{
return new UpstreamPathTemplate(RegExForwardSlashAndOnePlaceHolder, 0, false);
return new UpstreamPathTemplate(RegExForwardSlashAndOnePlaceHolder, 0, false, reRoute.UpstreamPathTemplate);
}
}
}
@ -60,7 +60,7 @@ namespace Ocelot.Configuration.Creator
if (upstreamTemplate == "/")
{
return new UpstreamPathTemplate(RegExForwardSlashOnly, reRoute.Priority, containsQueryString);
return new UpstreamPathTemplate(RegExForwardSlashOnly, reRoute.Priority, containsQueryString, reRoute.UpstreamPathTemplate);
}
if(upstreamTemplate.EndsWith("/"))
@ -72,7 +72,7 @@ namespace Ocelot.Configuration.Creator
? $"^{upstreamTemplate}{RegExMatchEndString}"
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
return new UpstreamPathTemplate(route, reRoute.Priority, containsQueryString);
return new UpstreamPathTemplate(route, reRoute.Priority, containsQueryString, reRoute.UpstreamPathTemplate);
}
private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List<string> placeholders, int postitionOfPlaceHolderClosingBracket)

View File

@ -8,7 +8,7 @@ namespace Ocelot.Configuration
{
public DownstreamReRoute(
string key,
PathTemplate upstreamPathTemplate,
UpstreamPathTemplate upstreamPathTemplate,
List<HeaderFindAndReplace> upstreamHeadersFindAndReplace,
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
List<DownstreamHostAndPort> downstreamAddresses,
@ -30,7 +30,7 @@ namespace Ocelot.Configuration
bool isAuthenticated,
bool isAuthorised,
AuthenticationOptions authenticationOptions,
PathTemplate downstreamPathTemplate,
DownstreamPathTemplate downstreamDownstreamPathTemplate,
string loadBalancerKey,
List<string> delegatingHandlers,
List<AddHeader> addHeadersToDownstream,
@ -63,13 +63,13 @@ namespace Ocelot.Configuration
IsAuthenticated = isAuthenticated;
IsAuthorised = isAuthorised;
AuthenticationOptions = authenticationOptions;
DownstreamPathTemplate = downstreamPathTemplate;
DownstreamDownstreamPathTemplate = downstreamDownstreamPathTemplate;
LoadBalancerKey = loadBalancerKey;
AddHeadersToUpstream = addHeadersToUpstream;
}
public string Key { get; }
public PathTemplate UpstreamPathTemplate { get; }
public UpstreamPathTemplate UpstreamPathTemplate { get; }
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace { get; }
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace { get; }
public List<DownstreamHostAndPort> DownstreamAddresses { get; }
@ -91,7 +91,7 @@ namespace Ocelot.Configuration
public bool IsAuthenticated { get; }
public bool IsAuthorised { get; }
public AuthenticationOptions AuthenticationOptions { get; }
public PathTemplate DownstreamPathTemplate { get; }
public DownstreamPathTemplate DownstreamDownstreamPathTemplate { get; }
public string LoadBalancerKey { get; }
public List<string> DelegatingHandlers { get; }
public List<AddHeader> AddHeadersToDownstream { get; }

View File

@ -1,15 +1,12 @@
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Configuration.Creator;
using Ocelot.Requester.QoS;
using Ocelot.Values;
namespace Ocelot.Configuration
namespace Ocelot.Configuration
{
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Values;
public class ReRoute
{
public ReRoute(List<DownstreamReRoute> downstreamReRoute,
PathTemplate upstreamPathTemplate,
List<HttpMethod> upstreamHttpMethod,
UpstreamPathTemplate upstreamTemplatePattern,
string upstreamHost,
@ -17,13 +14,11 @@ namespace Ocelot.Configuration
{
UpstreamHost = upstreamHost;
DownstreamReRoute = downstreamReRoute;
UpstreamPathTemplate = upstreamPathTemplate;
UpstreamHttpMethod = upstreamHttpMethod;
UpstreamTemplatePattern = upstreamTemplatePattern;
Aggregator = aggregator;
}
public PathTemplate UpstreamPathTemplate { get; private set; }
public UpstreamPathTemplate UpstreamTemplatePattern { get; private set; }
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
public string UpstreamHost { get; private set; }

View File

@ -55,7 +55,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
private DownstreamRoute GetPlaceholderNamesAndValues(string path, string query, ReRoute reRoute)
{
var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, query, reRoute.UpstreamPathTemplate.Value);
var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, query, reRoute.UpstreamTemplatePattern.OriginalValue);
return new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute);
}

View File

@ -10,7 +10,7 @@
public class DownstreamRouteProviderFactory : IDownstreamRouteProviderFactory
{
private readonly Dictionary<string, IDownstreamRouteProvider> _providers;
private IOcelotLogger _logger;
private readonly IOcelotLogger _logger;
public DownstreamRouteProviderFactory(IServiceProvider provider, IOcelotLoggerFactory factory)
{
@ -22,7 +22,7 @@
{
//todo - this is a bit hacky we are saying there are no reRoutes or there are reRoutes but none of them have
//an upstream path template which means they are dyanmic and service discovery is on...
if((!config.ReRoutes.Any() || config.ReRoutes.All(x => string.IsNullOrEmpty(x.UpstreamPathTemplate.Value))) && IsServiceDiscovery(config.ServiceProviderConfiguration))
if((!config.ReRoutes.Any() || config.ReRoutes.All(x => string.IsNullOrEmpty(x.UpstreamTemplatePattern?.OriginalValue))) && IsServiceDiscovery(config.ServiceProviderConfiguration))
{
_logger.LogInformation($"Selected {nameof(DownstreamRouteCreator)} as DownstreamRouteProvider for this request");
return _providers[nameof(DownstreamRouteCreator)];

View File

@ -51,7 +51,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
return;
}
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamDownstreamPathTemplate.Value));
Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");

View File

@ -14,7 +14,7 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
return regex.IsMatch(upstreamUrlPath)
? new OkResponse<UrlMatch>(new UrlMatch(true))
: new OkResponse<UrlMatch>(new UrlMatch(false));
}
}
return regex.IsMatch($"{upstreamUrlPath}{upstreamQueryString}")
? new OkResponse<UrlMatch>(new UrlMatch(true))

View File

@ -27,7 +27,7 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
public async Task Invoke(DownstreamContext context)
{
var response = _replacer
.Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
.Replace(context.DownstreamReRoute.DownstreamDownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
if (response.IsError)
{

View File

@ -8,11 +8,11 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
{
public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
{
public Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
public Response<DownstreamPath> Replace(DownstreamPathTemplate downstreamDownstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
{
var downstreamPath = new StringBuilder();
downstreamPath.Append(downstreamPathTemplate.Value);
downstreamPath.Append(downstreamDownstreamPathTemplate.Value);
foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues)
{
@ -22,4 +22,4 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
return new OkResponse<DownstreamPath>(new DownstreamPath(downstreamPath.ToString()));
}
}
}
}

View File

@ -7,6 +7,6 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
{
public interface IDownstreamPathPlaceholderReplacer
{
Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
Response<DownstreamPath> Replace(DownstreamPathTemplate downstreamDownstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
}
}
}

View File

@ -26,7 +26,7 @@ namespace Ocelot.Headers.Middleware
{
if (context.DownstreamReRoute.ClaimsToHeaders.Any())
{
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value} has instructions to convert claims to headers");
var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(context.DownstreamReRoute.ClaimsToHeaders, context.HttpContext.User.Claims, context.DownstreamRequest);

View File

@ -26,7 +26,7 @@ namespace Ocelot.QueryStrings.Middleware
{
if (context.DownstreamReRoute.ClaimsToQueries.Any())
{
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries");
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value} has instructions to convert claims to queries");
var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(context.DownstreamReRoute.ClaimsToQueries, context.HttpContext.User.Claims, context.DownstreamRequest);

View File

@ -34,7 +34,7 @@ namespace Ocelot.RateLimit.Middleware
// check if rate limiting is enabled
if (!context.DownstreamReRoute.EnableEndpointEndpointRateLimiting)
{
Logger.LogInformation($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamPathTemplate.Value}");
Logger.LogInformation($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value}");
await _next.Invoke(context);
return;
}
@ -45,7 +45,7 @@ namespace Ocelot.RateLimit.Middleware
// check white list
if (IsWhitelisted(identity, options))
{
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} is white listed from rate limiting");
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value} is white listed from rate limiting");
await _next.Invoke(context);
return;
}
@ -112,7 +112,7 @@ namespace Ocelot.RateLimit.Middleware
public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamReRoute downstreamReRoute)
{
Logger.LogInformation(
$"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { downstreamReRoute.UpstreamPathTemplate.Value }, TraceIdentifier {httpContext.TraceIdentifier}.");
$"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { downstreamReRoute.UpstreamPathTemplate.OriginalValue }, TraceIdentifier {httpContext.TraceIdentifier}.");
}
public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)

View File

@ -13,7 +13,7 @@ namespace Ocelot.Requester
private readonly IDelegatingHandlerHandlerFactory _factory;
private readonly IHttpClientCache _cacheHandlers;
private readonly IOcelotLogger _logger;
private string _cacheKey;
private DownstreamReRoute _cacheKey;
private HttpClient _httpClient;
private IHttpClient _client;
private readonly TimeSpan _defaultTimeout;
@ -34,7 +34,7 @@ namespace Ocelot.Requester
public IHttpClient Create(DownstreamContext context)
{
_cacheKey = GetCacheKey(context);
_cacheKey = context.DownstreamReRoute;
var httpClient = _cacheHandlers.Get(_cacheKey);
@ -51,7 +51,7 @@ namespace Ocelot.Requester
handler.ServerCertificateCustomValidationCallback = (request, certificate, chain, errors) => true;
_logger
.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {context.DownstreamReRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {context.DownstreamReRoute.DownstreamPathTemplate}");
.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {context.DownstreamReRoute.UpstreamPathTemplate}, DownstreamDownstreamPathTemplate: {context.DownstreamReRoute.DownstreamDownstreamPathTemplate}");
}
var timeout = context.DownstreamReRoute.QosOptions.TimeoutValue == 0
@ -127,14 +127,5 @@ namespace Ocelot.Requester
});
return httpMessageHandler;
}
private string GetCacheKey(DownstreamContext request)
{
var cacheKey = $"{request.DownstreamRequest.Method}:{request.DownstreamRequest.OriginalString}";
_logger.LogDebug($"Cache key for request is {cacheKey}");
return cacheKey;
}
}
}

View File

@ -1,10 +1,11 @@
namespace Ocelot.Requester
{
using System;
using Configuration;
public interface IHttpClientCache
{
IHttpClient Get(string key);
void Set(string key, IHttpClient handler, TimeSpan expirationTime);
IHttpClient Get(DownstreamReRoute key);
void Set(DownstreamReRoute key, IHttpClient handler, TimeSpan expirationTime);
}
}

View File

@ -2,22 +2,23 @@
{
using System;
using System.Collections.Concurrent;
using Configuration;
public class MemoryHttpClientCache : IHttpClientCache
{
private readonly ConcurrentDictionary<string, IHttpClient> _httpClientsCache;
private readonly ConcurrentDictionary<DownstreamReRoute, IHttpClient> _httpClientsCache;
public MemoryHttpClientCache()
{
_httpClientsCache = new ConcurrentDictionary<string, IHttpClient>();
_httpClientsCache = new ConcurrentDictionary<DownstreamReRoute, IHttpClient>();
}
public void Set(string key, IHttpClient client, TimeSpan expirationTime)
public void Set(DownstreamReRoute key, IHttpClient client, TimeSpan expirationTime)
{
_httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client);
}
public IHttpClient Get(string key)
public IHttpClient Get(DownstreamReRoute key)
{
//todo handle error?
return _httpClientsCache.TryGetValue(key, out var client) ? client : null;

View File

@ -27,7 +27,7 @@ namespace Ocelot.Requester.QoS
return new OkResponse<DelegatingHandler>(handler(request, _ocelotLoggerFactory));
}
return new ErrorResponse<DelegatingHandler>(new UnableToFindQoSProviderError($"could not find qosProvider for {request.DownstreamScheme}{request.DownstreamAddresses}{request.DownstreamPathTemplate}"));
return new ErrorResponse<DelegatingHandler>(new UnableToFindQoSProviderError($"could not find qosProvider for {request.DownstreamScheme}{request.DownstreamAddresses}{request.DownstreamDownstreamPathTemplate}"));
}
}
}

View File

@ -1,12 +1,12 @@
namespace Ocelot.Values
{
public class PathTemplate
{
public PathTemplate(string value)
{
Value = value;
}
public string Value { get; }
}
}
namespace Ocelot.Values
{
public class DownstreamPathTemplate
{
public DownstreamPathTemplate(string value)
{
Value = value;
}
public string Value { get; }
}
}

View File

@ -2,16 +2,20 @@ namespace Ocelot.Values
{
public class UpstreamPathTemplate
{
public UpstreamPathTemplate(string template, int priority, bool containsQueryString)
public UpstreamPathTemplate(string template, int priority, bool containsQueryString, string originalValue)
{
Template = template;
Priority = priority;
ContainsQueryString = containsQueryString;
OriginalValue = originalValue;
}
public string Template { get; }
public int Priority { get; }
public int Priority { get; }
public bool ContainsQueryString { get; }
public string OriginalValue { get; }
}
}