diff --git a/docs/features/delegatinghandlers.rst b/docs/features/delegatinghandlers.rst index a055f6da..80dfa16b 100644 --- a/docs/features/delegatinghandlers.rst +++ b/docs/features/delegatinghandlers.rst @@ -2,7 +2,7 @@ Delegating Handers ================== Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 `_ -and I decided that it was going to be useful in various ways. Since then we extended it in `GitHub #208 `_. +and I decided that it was going to be useful in various ways. Since then we extended it in `GitHub #264 `_. Usage ^^^^^ diff --git a/docs/features/routing.rst b/docs/features/routing.rst index 4215aa44..b414d6c5 100644 --- a/docs/features/routing.rst +++ b/docs/features/routing.rst @@ -139,4 +139,42 @@ The ReRoute above will only be matched when the host header value is somedomain. If you do not set UpstreamHost on a ReRoue then any host header can match it. This is basically a catch all and preservers existing functionality at the time of building the feature. This means that if you have two ReRoutes that are the same apart from the UpstreamHost where one is null and the other set. Ocelot will favour the one that has been set. -This feature was requested as part of `Issue 216 `_ . \ No newline at end of file +This feature was requested as part of `Issue 216 `_ . + +Priority +^^^^^^^^ + +In `Issue 270 `_ I finally decided to expose the ReRoute priority in +configuration.json. This means you can decide in what order you want your ReRoutes to match the Upstream HttpRequest. + +In order to get this working add the following to a ReRoute in configuration.json, 0 is just an example value here but will explain below. + +.. code-block:: json + + { + "Priority": 0 + } + +0 is the lowest priority, Ocelot will always use 0 for /{catchAll} ReRoutes and this is still hardcoded. After that you are free +to set any priority you wish. + +e.g. you could have + +.. code-block:: json + + { + "UpstreamPathTemplate": "/goods/{catchAll}" + "Priority": 0 + } + +and + +.. code-block:: json + + { + "UpstreamPathTemplate": "/goods/delete" + "Priority": 1 + } + +In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete ReRoute. Previously it would have +matched /goods/{catchAll} (because this is the first ReRoute in the list!). \ No newline at end of file diff --git a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs index 833c61db..dbf6a2d1 100644 --- a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs +++ b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs @@ -42,7 +42,7 @@ namespace Ocelot.Configuration.Creator if (upstreamTemplate == "/") { - return new UpstreamPathTemplate(RegExForwardSlashOnly, 1); + return new UpstreamPathTemplate(RegExForwardSlashOnly, reRoute.Priority); } if(upstreamTemplate.EndsWith("/")) @@ -54,7 +54,7 @@ namespace Ocelot.Configuration.Creator ? $"^{upstreamTemplate}{RegExMatchEndString}" : $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}"; - return new UpstreamPathTemplate(route, 1); + return new UpstreamPathTemplate(route, reRoute.Priority); } private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List placeholders, int postitionOfPlaceHolderClosingBracket) diff --git a/src/Ocelot/Configuration/File/FileAggregateReRoute.cs b/src/Ocelot/Configuration/File/FileAggregateReRoute.cs index 8c9eabba..ca5013b2 100644 --- a/src/Ocelot/Configuration/File/FileAggregateReRoute.cs +++ b/src/Ocelot/Configuration/File/FileAggregateReRoute.cs @@ -14,5 +14,7 @@ namespace Ocelot.Configuration.File { get { return new List {"Get"}; } } + + public int Priority {get;set;} = 1; } } diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index adc747f9..0b2a06f9 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -20,6 +20,7 @@ namespace Ocelot.Configuration.File UpstreamHeaderTransform = new Dictionary(); DownstreamHostAndPorts = new List(); DelegatingHandlers = new List(); + Priority = 1; } public string DownstreamPathTemplate { get; set; } @@ -46,5 +47,6 @@ namespace Ocelot.Configuration.File public string UpstreamHost { get; set; } public string Key { get;set; } public List DelegatingHandlers {get;set;} + public int Priority { get;set; } } } diff --git a/src/Ocelot/Configuration/File/IReRoute.cs b/src/Ocelot/Configuration/File/IReRoute.cs index 69128d3a..fb7e9313 100644 --- a/src/Ocelot/Configuration/File/IReRoute.cs +++ b/src/Ocelot/Configuration/File/IReRoute.cs @@ -4,5 +4,6 @@ { string UpstreamPathTemplate { get; set; } bool ReRouteIsCaseSensitive { get; set; } + int Priority {get;set;} } } diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs index 0a16e565..9110af12 100644 --- a/test/Ocelot.AcceptanceTests/RoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs @@ -872,6 +872,56 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_use_priority() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/goods/{url}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/goods/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 53879, + } + }, + Priority = 0 + }, + new FileReRoute + { + DownstreamPathTemplate = "/goods/delete", + DownstreamScheme = "http", + UpstreamPathTemplate = "/goods/delete", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 52879, + } + }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52879/", "/goods/delete", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/goods/delete")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) { _builder = new WebHostBuilder() diff --git a/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs index 9dce0e50..c70bd440 100644 --- a/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs @@ -19,6 +19,38 @@ namespace Ocelot.UnitTests.Configuration _creator = new UpstreamTemplatePatternCreator(); } + [Fact] + public void should_use_re_route_priority() + { + var fileReRoute = new FileReRoute + { + UpstreamPathTemplate = "/orders/{catchAll}", + Priority = 0 + }; + + this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^(?i)/orders/[0-9a-zA-Z].*$")) + .And(x => ThenThePriorityIs(0)) + .BDDfy(); + } + + [Fact] + public void should_use_zero_priority() + { + var fileReRoute = new FileReRoute + { + UpstreamPathTemplate = "/{catchAll}", + Priority = 1 + }; + + this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^/.*")) + .And(x => ThenThePriorityIs(0)) + .BDDfy(); + } + [Fact] public void should_set_upstream_template_pattern_to_ignore_case_sensitivity() {