mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 18:22:49 +08:00
Now defaults to case insensitive routing but you can override with a setting, also global request id setting available
This commit is contained in:
parent
30c668bfdf
commit
ff5776613f
31
README.md
31
README.md
@ -45,6 +45,18 @@ All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
|
|||||||
An example configuration can be found [here](https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/configuration.json)
|
An example configuration can be found [here](https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/configuration.json)
|
||||||
and an explained configuration can be found [here](https://github.com/TomPallister/Ocelot/blob/develop/configuration-explanation.txt). More detailed instructions to come on how to configure this.
|
and an explained configuration can be found [here](https://github.com/TomPallister/Ocelot/blob/develop/configuration-explanation.txt). More detailed instructions to come on how to configure this.
|
||||||
|
|
||||||
|
There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration.
|
||||||
|
The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global
|
||||||
|
configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful
|
||||||
|
if you don't want to manage lots of ReRoute specific settings.
|
||||||
|
|
||||||
|
{
|
||||||
|
"ReRoutes": [],
|
||||||
|
"GlobalConfiguration": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
More information on how to use these options is below..
|
||||||
|
|
||||||
## Startup
|
## Startup
|
||||||
|
|
||||||
An example startup using a json file for configuration can be seen below.
|
An example startup using a json file for configuration can be seen below.
|
||||||
@ -125,8 +137,14 @@ The placeholder needs to be in both the DownstreamTemplate and UpstreamTemplate.
|
|||||||
Ocelot will attempt to replace the placeholder with the correct variable value from the
|
Ocelot will attempt to replace the placeholder with the correct variable value from the
|
||||||
Upstream URL when the request comes in.
|
Upstream URL when the request comes in.
|
||||||
|
|
||||||
At the moment all Ocelot routing is case sensitive. I think I will turn this off by default
|
At the moment without any configuration Ocelot will default to all ReRoutes being case insensitive.
|
||||||
in the future with an options to make Ocelot case sensitive per ReRoute.
|
In order to change this you can specify on a per ReRoute basis the following setting.
|
||||||
|
|
||||||
|
"ReRouteIsCaseSensitive": true
|
||||||
|
|
||||||
|
This means that when Ocelot tries to match the incoming upstream url with an upstream template the
|
||||||
|
evaluation will be case sensitive. This setting defaults to false so only set it if you want
|
||||||
|
the ReRoute to be case sensitive is my advice!
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
@ -263,6 +281,13 @@ In order to use the requestid feature in your ReRoute configuration add this set
|
|||||||
|
|
||||||
In this example OcRequestId is the request header that contains the clients request id.
|
In this example OcRequestId is the request header that contains the clients request id.
|
||||||
|
|
||||||
|
There is also a setting in the GlobalConfiguration section which will override whatever has been
|
||||||
|
set at ReRoute level for the request id. The setting is as fllows.
|
||||||
|
|
||||||
|
"RequestIdKey": "OcRequestId",
|
||||||
|
|
||||||
|
It behaves in exactly the same way as the ReRoute level RequestIdKey settings.
|
||||||
|
|
||||||
## Caching
|
## Caching
|
||||||
|
|
||||||
Ocelot supports some very rudimentary caching at the moment provider by
|
Ocelot supports some very rudimentary caching at the moment provider by
|
||||||
@ -300,6 +325,8 @@ forwarded to the downstream service. Obviously this would break everything :(
|
|||||||
and doesnt check the response is OK. I think the fact you can even call stuff
|
and doesnt check the response is OK. I think the fact you can even call stuff
|
||||||
that isnt available is annoying. Let alone it be null.
|
that isnt available is annoying. Let alone it be null.
|
||||||
|
|
||||||
|
+ The Ocelot Request Id starts getting logged too late in the pipeline.
|
||||||
|
|
||||||
## Coming up
|
## Coming up
|
||||||
|
|
||||||
You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
|
You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
"ReRoutes": [
|
{
|
||||||
|
"ReRoutes": [
|
||||||
{
|
{
|
||||||
# The url we are forwarding the request to
|
# The url we are forwarding the request to
|
||||||
"UpstreamTemplate": "/identityserverexample",
|
"UpstreamTemplate": "/identityserverexample",
|
||||||
@ -66,5 +67,14 @@
|
|||||||
# a user id or something that would get ignored by the downstream service. This is a hack and I
|
# a user id or something that would get ignored by the downstream service. This is a hack and I
|
||||||
# intend to provide a mechanism the user can specify for the ttl caching. Also want to expand
|
# intend to provide a mechanism the user can specify for the ttl caching. Also want to expand
|
||||||
# the caching a lot.
|
# the caching a lot.
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
"FileCacheOptions": { "TtlSeconds": 15 },
|
||||||
|
# The value of this is used when matching the upstream template to an upstream url.
|
||||||
|
"ReRouteIsCaseSensitive": false
|
||||||
|
},
|
||||||
|
# This section is meant to be for global configuration settings
|
||||||
|
"GlobalConfiguration": {
|
||||||
|
# If this is set it will override any route specific request id keys, behaves the same
|
||||||
|
# otherwise
|
||||||
|
"RequestIdKey": "OcRequestId",
|
||||||
|
}
|
||||||
}
|
}
|
@ -19,6 +19,8 @@ namespace Ocelot.Configuration.Creator
|
|||||||
private readonly IConfigurationValidator _configurationValidator;
|
private readonly IConfigurationValidator _configurationValidator;
|
||||||
private const string RegExMatchEverything = ".*";
|
private const string RegExMatchEverything = ".*";
|
||||||
private const string RegExMatchEndString = "$";
|
private const string RegExMatchEndString = "$";
|
||||||
|
private const string RegExIgnoreCase = "(?i)";
|
||||||
|
|
||||||
private readonly IClaimToThingConfigurationParser _claimToThingConfigurationParser;
|
private readonly IClaimToThingConfigurationParser _claimToThingConfigurationParser;
|
||||||
private readonly ILogger<FileOcelotConfigurationCreator> _logger;
|
private readonly ILogger<FileOcelotConfigurationCreator> _logger;
|
||||||
|
|
||||||
@ -65,14 +67,56 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
foreach (var reRoute in _options.Value.ReRoutes)
|
foreach (var reRoute in _options.Value.ReRoutes)
|
||||||
{
|
{
|
||||||
var ocelotReRoute = SetUpReRoute(reRoute);
|
var ocelotReRoute = SetUpReRoute(reRoute, _options.Value.GlobalConfiguration);
|
||||||
reRoutes.Add(ocelotReRoute);
|
reRoutes.Add(ocelotReRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OcelotConfiguration(reRoutes);
|
return new OcelotConfiguration(reRoutes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReRoute SetUpReRoute(FileReRoute reRoute)
|
private ReRoute SetUpReRoute(FileReRoute reRoute, FileGlobalConfiguration globalConfiguration)
|
||||||
|
{
|
||||||
|
var globalRequestIdConfiguration = !string.IsNullOrEmpty(globalConfiguration?.RequestIdKey);
|
||||||
|
|
||||||
|
var upstreamTemplate = BuildUpstreamTemplate(reRoute);
|
||||||
|
|
||||||
|
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider);
|
||||||
|
|
||||||
|
var isAuthorised = reRoute.RouteClaimsRequirement?.Count > 0;
|
||||||
|
|
||||||
|
var isCached = reRoute.FileCacheOptions.TtlSeconds > 0;
|
||||||
|
|
||||||
|
var requestIdKey = globalRequestIdConfiguration
|
||||||
|
? globalConfiguration.RequestIdKey
|
||||||
|
: reRoute.RequestIdKey;
|
||||||
|
|
||||||
|
|
||||||
|
if (isAuthenticated)
|
||||||
|
{
|
||||||
|
var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
|
||||||
|
reRoute.AuthenticationOptions.ProviderRootUrl, reRoute.AuthenticationOptions.ScopeName,
|
||||||
|
reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes,
|
||||||
|
reRoute.AuthenticationOptions.ScopeSecret);
|
||||||
|
|
||||||
|
var claimsToHeaders = GetAddThingsToRequest(reRoute.AddHeadersToRequest);
|
||||||
|
var claimsToClaims = GetAddThingsToRequest(reRoute.AddClaimsToRequest);
|
||||||
|
var claimsToQueries = GetAddThingsToRequest(reRoute.AddQueriesToRequest);
|
||||||
|
|
||||||
|
return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
|
||||||
|
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
||||||
|
authOptionsForRoute, claimsToHeaders, claimsToClaims,
|
||||||
|
reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries,
|
||||||
|
requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
|
||||||
|
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
||||||
|
null, new List<ClaimToThing>(), new List<ClaimToThing>(),
|
||||||
|
reRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>(),
|
||||||
|
requestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildUpstreamTemplate(FileReRoute reRoute)
|
||||||
{
|
{
|
||||||
var upstreamTemplate = reRoute.UpstreamTemplate;
|
var upstreamTemplate = reRoute.UpstreamTemplate;
|
||||||
|
|
||||||
@ -94,38 +138,9 @@ namespace Ocelot.Configuration.Creator
|
|||||||
upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
|
upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
|
||||||
}
|
}
|
||||||
|
|
||||||
upstreamTemplate = $"{upstreamTemplate}{RegExMatchEndString}";
|
return reRoute.ReRouteIsCaseSensitive
|
||||||
|
? $"{upstreamTemplate}{RegExMatchEndString}"
|
||||||
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider);
|
: $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
|
||||||
|
|
||||||
var isAuthorised = reRoute.RouteClaimsRequirement?.Count > 0;
|
|
||||||
|
|
||||||
var isCached = reRoute.FileCacheOptions.TtlSeconds > 0;
|
|
||||||
|
|
||||||
if (isAuthenticated)
|
|
||||||
{
|
|
||||||
var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
|
|
||||||
reRoute.AuthenticationOptions.ProviderRootUrl, reRoute.AuthenticationOptions.ScopeName,
|
|
||||||
reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes,
|
|
||||||
reRoute.AuthenticationOptions.ScopeSecret);
|
|
||||||
|
|
||||||
var claimsToHeaders = GetAddThingsToRequest(reRoute.AddHeadersToRequest);
|
|
||||||
var claimsToClaims = GetAddThingsToRequest(reRoute.AddClaimsToRequest);
|
|
||||||
var claimsToQueries = GetAddThingsToRequest(reRoute.AddQueriesToRequest);
|
|
||||||
|
|
||||||
return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
|
|
||||||
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
|
||||||
authOptionsForRoute, claimsToHeaders, claimsToClaims,
|
|
||||||
reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries,
|
|
||||||
reRoute.RequestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
|
|
||||||
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
|
||||||
null, new List<ClaimToThing>(), new List<ClaimToThing>(),
|
|
||||||
reRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>(),
|
|
||||||
reRoute.RequestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
|
private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
|
||||||
|
@ -7,8 +7,10 @@ namespace Ocelot.Configuration.File
|
|||||||
public FileConfiguration()
|
public FileConfiguration()
|
||||||
{
|
{
|
||||||
ReRoutes = new List<FileReRoute>();
|
ReRoutes = new List<FileReRoute>();
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileReRoute> ReRoutes { get; set; }
|
public List<FileReRoute> ReRoutes { get; set; }
|
||||||
|
public FileGlobalConfiguration GlobalConfiguration { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
src/Ocelot/Configuration/File/FileGlobalConfiguration.cs
Normal file
7
src/Ocelot/Configuration/File/FileGlobalConfiguration.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ocelot.Configuration.File
|
||||||
|
{
|
||||||
|
public class FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
public string RequestIdKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -24,5 +24,6 @@ namespace Ocelot.Configuration.File
|
|||||||
public Dictionary<string, string> AddQueriesToRequest { get; set; }
|
public Dictionary<string, string> AddQueriesToRequest { get; set; }
|
||||||
public string RequestIdKey { get; set; }
|
public string RequestIdKey { get; set; }
|
||||||
public FileCacheOptions FileCacheOptions { get; set; }
|
public FileCacheOptions FileCacheOptions { get; set; }
|
||||||
|
public bool ReRouteIsCaseSensitive { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
200
test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs
Normal file
200
test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.AcceptanceTests
|
||||||
|
{
|
||||||
|
public class CaseSensitiveRoutingTests : IDisposable
|
||||||
|
{
|
||||||
|
private IWebHost _builder;
|
||||||
|
private readonly Steps _steps;
|
||||||
|
|
||||||
|
public CaseSensitiveRoutingTests()
|
||||||
|
{
|
||||||
|
_steps = new Steps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_when_global_ignore_case_sensitivity_set()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51879/api/products/{productId}",
|
||||||
|
UpstreamTemplate = "/products/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_when_reroute_ignore_case_sensitivity_set()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51879/api/products/{productId}",
|
||||||
|
UpstreamTemplate = "/products/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_404_when_reroute_respect_case_sensitivity_set()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51879/api/products/{productId}",
|
||||||
|
UpstreamTemplate = "/products/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_when_reroute_respect_case_sensitivity_set()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51879/api/products/{productId}",
|
||||||
|
UpstreamTemplate = "/PRODUCTS/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_404_when_global_respect_case_sensitivity_set()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51879/api/products/{productId}",
|
||||||
|
UpstreamTemplate = "/products/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_when_global_respect_case_sensitivity_set()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51879/api/products/{productId}",
|
||||||
|
UpstreamTemplate = "/PRODUCTS/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
||||||
|
{
|
||||||
|
_builder = new WebHostBuilder()
|
||||||
|
.UseUrls(url)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.UseUrls(url)
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
await context.Response.WriteAsync(responseBody);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_builder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_builder?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -76,6 +76,36 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_use_global_request_id_and_forward()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51879/",
|
||||||
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
RequestIdKey = _steps.RequestIdKey
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var requestId = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId))
|
||||||
|
.Then(x => _steps.ThenTheRequestIdIsReturned(requestId))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string url)
|
private void GivenThereIsAServiceRunningOn(string url)
|
||||||
{
|
{
|
||||||
_builder = new WebHostBuilder()
|
_builder = new WebHostBuilder()
|
||||||
|
@ -1 +1 @@
|
|||||||
{"ReRoutes":[{"DownstreamTemplate":"http://localhost:41879/","UpstreamTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0}}]}
|
{"ReRoutes":[{"DownstreamTemplate":"http://localhost:41879/","UpstreamTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false}],"GlobalConfiguration":{"RequestIdKey":null}}
|
@ -36,7 +36,37 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_create_template_pattern_that_matches_anything_to_end_of_string()
|
public void should_use_reroute_case_sensitivity_value()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
UpstreamTemplate = "/api/products/{productId}",
|
||||||
|
DownstreamTemplate = "/products/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.And(x => x.GivenTheConfigIsValid())
|
||||||
|
.When(x => x.WhenICreateTheConfig())
|
||||||
|
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamTemplate("/products/{productId}")
|
||||||
|
.WithUpstreamTemplate("/api/products/{productId}")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.WithUpstreamTemplatePattern("(?i)/api/products/.*$")
|
||||||
|
.Build()
|
||||||
|
}))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_set_upstream_template_pattern_to_ignore_case_sensitivity()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -49,6 +79,101 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
UpstreamHttpMethod = "Get"
|
UpstreamHttpMethod = "Get"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}))
|
||||||
|
.And(x => x.GivenTheConfigIsValid())
|
||||||
|
.When(x => x.WhenICreateTheConfig())
|
||||||
|
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamTemplate("/products/{productId}")
|
||||||
|
.WithUpstreamTemplate("/api/products/{productId}")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.WithUpstreamTemplatePattern("(?i)/api/products/.*$")
|
||||||
|
.Build()
|
||||||
|
}))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_set_upstream_template_pattern_to_respect_case_sensitivity()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
UpstreamTemplate = "/api/products/{productId}",
|
||||||
|
DownstreamTemplate = "/products/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.And(x => x.GivenTheConfigIsValid())
|
||||||
|
.When(x => x.WhenICreateTheConfig())
|
||||||
|
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamTemplate("/products/{productId}")
|
||||||
|
.WithUpstreamTemplate("/api/products/{productId}")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.WithUpstreamTemplatePattern("/api/products/.*$")
|
||||||
|
.Build()
|
||||||
|
}))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_set_global_request_id_key()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
UpstreamTemplate = "/api/products/{productId}",
|
||||||
|
DownstreamTemplate = "/products/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
RequestIdKey = "blahhhh"
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.And(x => x.GivenTheConfigIsValid())
|
||||||
|
.When(x => x.WhenICreateTheConfig())
|
||||||
|
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRouteBuilder()
|
||||||
|
.WithDownstreamTemplate("/products/{productId}")
|
||||||
|
.WithUpstreamTemplate("/api/products/{productId}")
|
||||||
|
.WithUpstreamHttpMethod("Get")
|
||||||
|
.WithUpstreamTemplatePattern("/api/products/.*$")
|
||||||
|
.WithRequestIdKey("blahhhh")
|
||||||
|
.Build()
|
||||||
|
}))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_create_template_pattern_that_matches_anything_to_end_of_string()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
UpstreamTemplate = "/api/products/{productId}",
|
||||||
|
DownstreamTemplate = "/products/{productId}",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
.And(x => x.GivenTheConfigIsValid())
|
.And(x => x.GivenTheConfigIsValid())
|
||||||
.When(x => x.WhenICreateTheConfig())
|
.When(x => x.WhenICreateTheConfig())
|
||||||
@ -95,6 +220,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamTemplate = "/api/products/{productId}",
|
||||||
DownstreamTemplate = "/products/{productId}",
|
DownstreamTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true,
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AdditionalScopes = new List<string>(),
|
AdditionalScopes = new List<string>(),
|
||||||
@ -153,6 +279,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
UpstreamTemplate = "/api/products/{productId}",
|
UpstreamTemplate = "/api/products/{productId}",
|
||||||
DownstreamTemplate = "/products/{productId}",
|
DownstreamTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get",
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true,
|
||||||
AuthenticationOptions = new FileAuthenticationOptions
|
AuthenticationOptions = new FileAuthenticationOptions
|
||||||
{
|
{
|
||||||
AdditionalScopes = new List<string>(),
|
AdditionalScopes = new List<string>(),
|
||||||
@ -183,7 +310,8 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}/variants/{variantId}",
|
UpstreamTemplate = "/api/products/{productId}/variants/{variantId}",
|
||||||
DownstreamTemplate = "/products/{productId}",
|
DownstreamTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get"
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -212,7 +340,8 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
UpstreamTemplate = "/api/products/{productId}/variants/{variantId}/",
|
UpstreamTemplate = "/api/products/{productId}/variants/{variantId}/",
|
||||||
DownstreamTemplate = "/products/{productId}",
|
DownstreamTemplate = "/products/{productId}",
|
||||||
UpstreamHttpMethod = "Get"
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -241,7 +370,8 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
UpstreamTemplate = "/",
|
UpstreamTemplate = "/",
|
||||||
DownstreamTemplate = "/api/products/",
|
DownstreamTemplate = "/api/products/",
|
||||||
UpstreamHttpMethod = "Get"
|
UpstreamHttpMethod = "Get",
|
||||||
|
ReRouteIsCaseSensitive = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
@ -128,6 +128,27 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_ignore_case_sensitivity()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/"))
|
||||||
|
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("(?i)api/product/products/.*/categories/.*/variant/$"))
|
||||||
|
.When(x => x.WhenIMatchThePaths())
|
||||||
|
.Then(x => x.ThenTheResultIsTrue())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_respect_case_sensitivity()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/"))
|
||||||
|
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/categories/.*/variant/$"))
|
||||||
|
.When(x => x.WhenIMatchThePaths())
|
||||||
|
.Then(x => x.ThenTheResultIsFalse())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void GivenIHaveAUpstreamPath(string downstreamPath)
|
private void GivenIHaveAUpstreamPath(string downstreamPath)
|
||||||
{
|
{
|
||||||
_downstreamUrlPath = downstreamPath;
|
_downstreamUrlPath = downstreamPath;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user