Merge pull request #122 from TomPallister/feature/fix-always-adding-trailing-slash

fixes issue #117
This commit is contained in:
Tom Pallister 2017-08-29 21:02:39 +01:00 committed by GitHub
commit 6f5c2967f4
4 changed files with 81 additions and 4 deletions

View File

@ -6,6 +6,7 @@ using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Utilities;
namespace Ocelot.DownstreamRouteFinder.Finder namespace Ocelot.DownstreamRouteFinder.Finder
{ {
@ -24,6 +25,8 @@ namespace Ocelot.DownstreamRouteFinder.Finder
public async Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod) public async Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
{ {
upstreamUrlPath = upstreamUrlPath.SetLastCharacterAs('/');
var configuration = await _configProvider.Get(); var configuration = await _configProvider.Get();
var applicableReRoutes = configuration.Data.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower())); var applicableReRoutes = configuration.Data.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower()));

View File

@ -30,7 +30,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
var upstreamUrlPath = context.Request.Path.ToString().SetLastCharacterAs('/'); var upstreamUrlPath = context.Request.Path.ToString();
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath); _logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);

View File

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
@ -15,6 +16,7 @@ namespace Ocelot.AcceptanceTests
{ {
private IWebHost _builder; private IWebHost _builder;
private readonly Steps _steps; private readonly Steps _steps;
private string _downstreamPath;
public RoutingTests() public RoutingTests()
{ {
@ -232,6 +234,34 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_not_add_trailing_slash_to_downstream_url()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/products/{productId}",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(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 => ThenTheDownstreamUrlPathShouldBe("/api/products/1"))
.BDDfy();
}
[Fact] [Fact]
public void should_return_response_201_with_simple_url() public void should_return_response_201_with_simple_url()
{ {
@ -379,11 +409,11 @@ namespace Ocelot.AcceptanceTests
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseUrls(url)
.Configure(app => .Configure(app =>
{ {
app.Run(async context => app.Run(async context =>
{ {
_downstreamPath = context.Request.PathBase.Value;
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody); await context.Response.WriteAsync(responseBody);
}); });
@ -393,6 +423,11 @@ namespace Ocelot.AcceptanceTests
_builder.Start(); _builder.Start();
} }
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
{
_downstreamPath.ShouldBe(expectedDownstreamPath);
}
public void Dispose() public void Dispose()
{ {
_builder?.Dispose(); _builder?.Dispose();

View File

@ -36,7 +36,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [Fact]
public void should_return_route() public void should_return_route()
{ {
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
.And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<UrlPathPlaceholderNameAndValue>>( new OkResponse<List<UrlPathPlaceholderNameAndValue>>(
new List<UrlPathPlaceholderNameAndValue>()))) new List<UrlPathPlaceholderNameAndValue>())))
@ -65,6 +65,39 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_append_slash_to_upstream_url_path()
{
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher"))
.And(x =>x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<UrlPathPlaceholderNameAndValue>>(
new List<UrlPathPlaceholderNameAndValue>())))
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
{
new ReRouteBuilder()
.WithDownstreamPathTemplate("someDownstreamPath")
.WithUpstreamPathTemplate("someUpstreamPath")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("someUpstreamPath")
.Build()
}, string.Empty
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
.When(x => x.WhenICallTheFinder())
.Then(
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(
new List<UrlPathPlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamPathTemplate("someDownstreamPath")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build()
)))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher/"))
.BDDfy();
}
[Fact] [Fact]
public void should_return_route_if_upstream_path_and_upstream_template_are_the_same() public void should_return_route_if_upstream_path_and_upstream_template_are_the_same()
{ {
@ -137,7 +170,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [Fact]
public void should_not_return_route() public void should_not_return_route()
{ {
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/"))
.And(x => x.GivenTheConfigurationIs(new List<ReRoute> .And(x => x.GivenTheConfigurationIs(new List<ReRoute>
{ {
new ReRouteBuilder() new ReRouteBuilder()
@ -269,6 +302,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once);
} }
private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath)
{
_mockMatcher
.Verify(x => x.Match(expectedUpstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once);
}
private void ThenTheUrlMatcherIsNotCalled() private void ThenTheUrlMatcherIsNotCalled()
{ {
_mockMatcher _mockMatcher