mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 07:48:16 +08:00
Changed routing to support a catch all style (#187)
* Changed routing to support a catch all style * refactoring placeholder tuff * implemented simple priority in the routing
This commit is contained in:
@ -2,6 +2,7 @@
|
||||
using System.Net.Http;
|
||||
using Ocelot.Values;
|
||||
using System.Linq;
|
||||
using Ocelot.Configuration.Creator;
|
||||
|
||||
namespace Ocelot.Configuration.Builder
|
||||
{
|
||||
@ -11,7 +12,7 @@ namespace Ocelot.Configuration.Builder
|
||||
private string _loadBalancerKey;
|
||||
private string _downstreamPathTemplate;
|
||||
private string _upstreamTemplate;
|
||||
private string _upstreamTemplatePattern;
|
||||
private UpstreamPathTemplate _upstreamTemplatePattern;
|
||||
private List<HttpMethod> _upstreamHttpMethod;
|
||||
private bool _isAuthenticated;
|
||||
private List<ClaimToThing> _configHeaderExtractorProperties;
|
||||
@ -65,7 +66,7 @@ namespace Ocelot.Configuration.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReRouteBuilder WithUpstreamTemplatePattern(string input)
|
||||
public ReRouteBuilder WithUpstreamTemplatePattern(UpstreamPathTemplate input)
|
||||
{
|
||||
_upstreamTemplatePattern = input;
|
||||
return this;
|
||||
|
@ -1,9 +1,10 @@
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public interface IUpstreamTemplatePatternCreator
|
||||
{
|
||||
string Create(FileReRoute reRoute);
|
||||
UpstreamPathTemplate Create(FileReRoute reRoute);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
@ -11,7 +12,7 @@ namespace Ocelot.Configuration.Creator
|
||||
private const string RegExForwardSlashOnly = "^/$";
|
||||
private const string RegExForwardSlashAndOnePlaceHolder = "^/.*";
|
||||
|
||||
public string Create(FileReRoute reRoute)
|
||||
public UpstreamPathTemplate Create(FileReRoute reRoute)
|
||||
{
|
||||
var upstreamTemplate = reRoute.UpstreamPathTemplate;
|
||||
|
||||
@ -26,9 +27,10 @@ namespace Ocelot.Configuration.Creator
|
||||
var placeHolderName = upstreamTemplate.Substring(i, difference);
|
||||
placeholders.Add(placeHolderName);
|
||||
|
||||
//hack to handle /{url} case
|
||||
if(ForwardSlashAndOnePlaceHolder(upstreamTemplate, placeholders, postitionOfPlaceHolderClosingBracket))
|
||||
{
|
||||
return RegExForwardSlashAndOnePlaceHolder;
|
||||
return new UpstreamPathTemplate(RegExForwardSlashAndOnePlaceHolder, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,7 +42,7 @@ namespace Ocelot.Configuration.Creator
|
||||
|
||||
if (upstreamTemplate == "/")
|
||||
{
|
||||
return RegExForwardSlashOnly;
|
||||
return new UpstreamPathTemplate(RegExForwardSlashOnly, 1);
|
||||
}
|
||||
|
||||
if(upstreamTemplate.EndsWith("/"))
|
||||
@ -52,7 +54,7 @@ namespace Ocelot.Configuration.Creator
|
||||
? $"^{upstreamTemplate}{RegExMatchEndString}"
|
||||
: $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
|
||||
|
||||
return route;
|
||||
return new UpstreamPathTemplate(route, 1);
|
||||
}
|
||||
|
||||
private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List<string> placeholders, int postitionOfPlaceHolderClosingBracket)
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.Configuration
|
||||
@ -9,7 +10,7 @@ namespace Ocelot.Configuration
|
||||
public ReRoute(PathTemplate downstreamPathTemplate,
|
||||
PathTemplate upstreamPathTemplate,
|
||||
List<HttpMethod> upstreamHttpMethod,
|
||||
string upstreamTemplatePattern,
|
||||
UpstreamPathTemplate upstreamTemplatePattern,
|
||||
bool isAuthenticated,
|
||||
AuthenticationOptions authenticationOptions,
|
||||
List<ClaimToThing> claimsToHeaders,
|
||||
@ -67,7 +68,7 @@ namespace Ocelot.Configuration
|
||||
public string ReRouteKey {get;private set;}
|
||||
public PathTemplate DownstreamPathTemplate { get; private set; }
|
||||
public PathTemplate UpstreamPathTemplate { get; private set; }
|
||||
public string UpstreamTemplatePattern { get; private set; }
|
||||
public UpstreamPathTemplate UpstreamTemplatePattern { get; private set; }
|
||||
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
|
||||
public bool IsAuthenticated { get; private set; }
|
||||
public bool IsAuthorised { get; private set; }
|
||||
|
@ -108,7 +108,7 @@ namespace Ocelot.DependencyInjection
|
||||
_services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
|
||||
_services.TryAddSingleton<IClaimsParser, ClaimsParser>();
|
||||
_services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
|
||||
_services.TryAddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
|
||||
_services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
|
||||
_services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
|
||||
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
|
||||
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
||||
|
@ -6,12 +6,12 @@ namespace Ocelot.DownstreamRouteFinder
|
||||
{
|
||||
public class DownstreamRoute
|
||||
{
|
||||
public DownstreamRoute(List<UrlPathPlaceholderNameAndValue> templatePlaceholderNameAndValues, ReRoute reRoute)
|
||||
public DownstreamRoute(List<PlaceholderNameAndValue> templatePlaceholderNameAndValues, ReRoute reRoute)
|
||||
{
|
||||
TemplatePlaceholderNameAndValues = templatePlaceholderNameAndValues;
|
||||
ReRoute = reRoute;
|
||||
}
|
||||
public List<UrlPathPlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; private set; }
|
||||
public List<PlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; private set; }
|
||||
public ReRoute ReRoute { get; private set; }
|
||||
}
|
||||
}
|
@ -13,34 +13,30 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
public class DownstreamRouteFinder : IDownstreamRouteFinder
|
||||
{
|
||||
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
|
||||
private readonly IUrlPathPlaceholderNameAndValueFinder _urlPathPlaceholderNameAndValueFinder;
|
||||
private readonly IPlaceholderNameAndValueFinder __placeholderNameAndValueFinder;
|
||||
|
||||
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IUrlPathPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
|
||||
public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
|
||||
{
|
||||
_urlMatcher = urlMatcher;
|
||||
_urlPathPlaceholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
|
||||
__placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
|
||||
}
|
||||
|
||||
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod, IOcelotConfiguration configuration)
|
||||
public Response<DownstreamRoute> FindDownstreamRoute(string path, string httpMethod, IOcelotConfiguration configuration)
|
||||
{
|
||||
var applicableReRoutes = configuration.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower()));
|
||||
var applicableReRoutes = configuration.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower())).OrderByDescending(x => x.UpstreamTemplatePattern.Priority);
|
||||
|
||||
foreach (var reRoute in applicableReRoutes)
|
||||
{
|
||||
if (upstreamUrlPath == reRoute.UpstreamTemplatePattern)
|
||||
if (path == reRoute.UpstreamTemplatePattern.Template)
|
||||
{
|
||||
var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value);
|
||||
|
||||
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
|
||||
return GetPlaceholderNamesAndValues(path, reRoute);
|
||||
}
|
||||
|
||||
var urlMatch = _urlMatcher.Match(upstreamUrlPath, reRoute.UpstreamTemplatePattern);
|
||||
var urlMatch = _urlMatcher.Match(path, reRoute.UpstreamTemplatePattern.Template);
|
||||
|
||||
if (urlMatch.Data.Match)
|
||||
{
|
||||
var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamPathTemplate.Value);
|
||||
|
||||
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
|
||||
return GetPlaceholderNamesAndValues(path, reRoute);
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,5 +45,12 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
new UnableToFindDownstreamRouteError()
|
||||
});
|
||||
}
|
||||
|
||||
private OkResponse<DownstreamRoute> GetPlaceholderNamesAndValues(string path, ReRoute reRoute)
|
||||
{
|
||||
var templatePlaceholderNameAndValues = __placeholderNameAndValueFinder.Find(path, reRoute.UpstreamPathTemplate.Value);
|
||||
|
||||
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute));
|
||||
}
|
||||
}
|
||||
}
|
@ -3,8 +3,8 @@ using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public interface IUrlPathPlaceholderNameAndValueFinder
|
||||
public interface IPlaceholderNameAndValueFinder
|
||||
{
|
||||
Response<List<UrlPathPlaceholderNameAndValue>> Find(string upstreamUrlPath, string upstreamUrlPathTemplate);
|
||||
Response<List<PlaceholderNameAndValue>> Find(string path, string pathTemplate);
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public class UrlPathPlaceholderNameAndValue
|
||||
public class PlaceholderNameAndValue
|
||||
{
|
||||
public UrlPathPlaceholderNameAndValue(string templateVariableName, string templateVariableValue)
|
||||
public PlaceholderNameAndValue(string name, string value)
|
||||
{
|
||||
TemplateVariableName = templateVariableName;
|
||||
TemplateVariableValue = templateVariableValue;
|
||||
Name = name;
|
||||
Value = value;
|
||||
}
|
||||
public string TemplateVariableName {get;private set;}
|
||||
public string TemplateVariableValue {get;private set;}
|
||||
public string Name {get;private set;}
|
||||
public string Value {get;private set;}
|
||||
}
|
||||
}
|
@ -3,44 +3,72 @@ using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public class UrlPathPlaceholderNameAndValueFinder : IUrlPathPlaceholderNameAndValueFinder
|
||||
public class UrlPathPlaceholderNameAndValueFinder : IPlaceholderNameAndValueFinder
|
||||
{
|
||||
public Response<List<UrlPathPlaceholderNameAndValue>> Find(string upstreamUrlPath, string upstreamUrlPathTemplate)
|
||||
public Response<List<PlaceholderNameAndValue>> Find(string path, string pathTemplate)
|
||||
{
|
||||
var templateKeysAndValues = new List<UrlPathPlaceholderNameAndValue>();
|
||||
var placeHolderNameAndValues = new List<PlaceholderNameAndValue>();
|
||||
|
||||
int counterForUrl = 0;
|
||||
int counterForPath = 0;
|
||||
|
||||
for (int counterForTemplate = 0; counterForTemplate < upstreamUrlPathTemplate.Length; counterForTemplate++)
|
||||
for (int counterForTemplate = 0; counterForTemplate < pathTemplate.Length; counterForTemplate++)
|
||||
{
|
||||
if ((upstreamUrlPath.Length > counterForUrl) && CharactersDontMatch(upstreamUrlPathTemplate[counterForTemplate], upstreamUrlPath[counterForUrl]) && ContinueScanningUrl(counterForUrl,upstreamUrlPath.Length))
|
||||
if ((path.Length > counterForPath) && CharactersDontMatch(pathTemplate[counterForTemplate], path[counterForPath]) && ContinueScanningUrl(counterForPath,path.Length))
|
||||
{
|
||||
if (IsPlaceholder(upstreamUrlPathTemplate[counterForTemplate]))
|
||||
if (IsPlaceholder(pathTemplate[counterForTemplate]))
|
||||
{
|
||||
var variableName = GetPlaceholderVariableName(upstreamUrlPathTemplate, counterForTemplate);
|
||||
var placeholderName = GetPlaceholderName(pathTemplate, counterForTemplate);
|
||||
|
||||
var variableValue = GetPlaceholderVariableValue(upstreamUrlPathTemplate, variableName, upstreamUrlPath, counterForUrl);
|
||||
var placeholderValue = GetPlaceholderValue(pathTemplate, placeholderName, path, counterForPath);
|
||||
|
||||
var templateVariableNameAndValue = new UrlPathPlaceholderNameAndValue(variableName, variableValue);
|
||||
placeHolderNameAndValues.Add(new PlaceholderNameAndValue(placeholderName, placeholderValue));
|
||||
|
||||
templateKeysAndValues.Add(templateVariableNameAndValue);
|
||||
counterForTemplate = GetNextCounterPosition(pathTemplate, counterForTemplate, '}');
|
||||
|
||||
counterForTemplate = GetNextCounterPosition(upstreamUrlPathTemplate, counterForTemplate, '}');
|
||||
|
||||
counterForUrl = GetNextCounterPosition(upstreamUrlPath, counterForUrl, '/');
|
||||
counterForPath = GetNextCounterPosition(path, counterForPath, '/');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return new OkResponse<List<UrlPathPlaceholderNameAndValue>>(templateKeysAndValues);
|
||||
return new OkResponse<List<PlaceholderNameAndValue>>(placeHolderNameAndValues);
|
||||
}
|
||||
counterForUrl++;
|
||||
else if(IsCatchAll(path, counterForPath, pathTemplate))
|
||||
{
|
||||
var endOfPlaceholder = GetNextCounterPosition(pathTemplate, counterForTemplate, '}');
|
||||
|
||||
var placeholderName = GetPlaceholderName(pathTemplate, 1);
|
||||
|
||||
if(NothingAfterFirstForwardSlash(path))
|
||||
{
|
||||
placeHolderNameAndValues.Add(new PlaceholderNameAndValue(placeholderName, ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
var placeholderValue = GetPlaceholderValue(pathTemplate, placeholderName, path, counterForPath + 1);
|
||||
placeHolderNameAndValues.Add(new PlaceholderNameAndValue(placeholderName, placeholderValue));
|
||||
}
|
||||
|
||||
counterForTemplate = endOfPlaceholder;
|
||||
}
|
||||
counterForPath++;
|
||||
}
|
||||
|
||||
return new OkResponse<List<UrlPathPlaceholderNameAndValue>>(templateKeysAndValues);
|
||||
return new OkResponse<List<PlaceholderNameAndValue>>(placeHolderNameAndValues);
|
||||
}
|
||||
|
||||
private string GetPlaceholderVariableValue(string urlPathTemplate, string variableName, string urlPath, int counterForUrl)
|
||||
private bool IsCatchAll(string path, int counterForPath, string pathTemplate)
|
||||
{
|
||||
return string.IsNullOrEmpty(path) || (path.Length > counterForPath && path[counterForPath] == '/') && pathTemplate.Length > 1
|
||||
&& pathTemplate.Substring(0, 2) == "/{"
|
||||
&& pathTemplate.IndexOf('}') == pathTemplate.Length - 1;
|
||||
}
|
||||
|
||||
private bool NothingAfterFirstForwardSlash(string path)
|
||||
{
|
||||
return path.Length == 1 || path.Length == 0;
|
||||
}
|
||||
|
||||
private string GetPlaceholderValue(string urlPathTemplate, string variableName, string urlPath, int counterForUrl)
|
||||
{
|
||||
var positionOfNextSlash = urlPath.IndexOf('/', counterForUrl);
|
||||
|
||||
@ -54,7 +82,7 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
return variableValue;
|
||||
}
|
||||
|
||||
private string GetPlaceholderVariableName(string urlPathTemplate, int counterForTemplate)
|
||||
private string GetPlaceholderName(string urlPathTemplate, int counterForTemplate)
|
||||
{
|
||||
var postitionOfPlaceHolderClosingBracket = urlPathTemplate.IndexOf('}', counterForTemplate) + 1;
|
||||
|
||||
|
@ -8,7 +8,7 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
||||
{
|
||||
public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
|
||||
{
|
||||
public Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
|
||||
public Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
|
||||
{
|
||||
var downstreamPath = new StringBuilder();
|
||||
|
||||
@ -16,7 +16,7 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
||||
|
||||
foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues)
|
||||
{
|
||||
downstreamPath.Replace(placeholderVariableAndValue.TemplateVariableName, placeholderVariableAndValue.TemplateVariableValue);
|
||||
downstreamPath.Replace(placeholderVariableAndValue.Name, placeholderVariableAndValue.Value);
|
||||
}
|
||||
|
||||
return new OkResponse<DownstreamPath>(new DownstreamPath(downstreamPath.ToString()));
|
||||
|
@ -7,6 +7,6 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
||||
{
|
||||
public interface IDownstreamPathPlaceholderReplacer
|
||||
{
|
||||
Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
|
||||
Response<DownstreamPath> Replace(PathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
|
||||
}
|
||||
}
|
14
src/Ocelot/Values/UpstreamPathTemplate.cs
Normal file
14
src/Ocelot/Values/UpstreamPathTemplate.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace Ocelot.Values
|
||||
{
|
||||
public class UpstreamPathTemplate
|
||||
{
|
||||
public UpstreamPathTemplate(string template, int priority)
|
||||
{
|
||||
Template = template;
|
||||
Priority = priority;
|
||||
}
|
||||
|
||||
public string Template {get;}
|
||||
public int Priority {get;}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user