Merge pull request #1 from TomPallister/develop

mergecode
This commit is contained in:
geffzhang 2016-12-18 13:14:20 +08:00 committed by GitHub
commit 86414c6917
11 changed files with 190 additions and 22 deletions

View File

@ -122,6 +122,8 @@ Ocelot's primary functionality is to take incomeing http requests and forward th
to a downstream service. At the moment in the form of another http request (in the future to a downstream service. At the moment in the form of another http request (in the future
this could be any transport mechanism.). this could be any transport mechanism.).
Ocelot always adds a trailing slash to an UpstreamTemplate.
Ocelot's describes the routing of one request to another as a ReRoute. In order to get Ocelot's describes the routing of one request to another as a ReRoute. In order to get
anything working in Ocelot you need to set up a ReRoute in the configuration. anything working in Ocelot you need to set up a ReRoute in the configuration.

View File

@ -1,9 +1,11 @@
{ {
"ReRoutes": [ "ReRoutes": [
{ {
# The url we are forwarding the request to # The url we are forwarding the request to, ocelot will not add a trailing slash
"UpstreamTemplate": "/identityserverexample", "DownstreamTemplate": "http://somehost.com/identityserverexample",
# The path we are listening on for this re route # The path we are listening on for this re route, Ocelot will add a trailing slash to
# this property. Then when a request is made Ocelot makes sure a trailing slash is added
# to that so everything matches
"UpstreamTemplate": "/identityserverexample", "UpstreamTemplate": "/identityserverexample",
# The method we are listening for on this re route # The method we are listening for on this re route
"UpstreamHttpMethod": "Get", "UpstreamHttpMethod": "Get",

View File

@ -7,6 +7,7 @@ using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser; using Ocelot.Configuration.Parser;
using Ocelot.Configuration.Validator; using Ocelot.Configuration.Validator;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Utilities;
namespace Ocelot.Configuration.Creator namespace Ocelot.Configuration.Creator
{ {
@ -90,7 +91,6 @@ namespace Ocelot.Configuration.Creator
? globalConfiguration.RequestIdKey ? globalConfiguration.RequestIdKey
: reRoute.RequestIdKey; : reRoute.RequestIdKey;
if (isAuthenticated) if (isAuthenticated)
{ {
var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider, var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
@ -120,6 +120,8 @@ namespace Ocelot.Configuration.Creator
{ {
var upstreamTemplate = reRoute.UpstreamTemplate; var upstreamTemplate = reRoute.UpstreamTemplate;
upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/');
var placeholders = new List<string>(); var placeholders = new List<string>();
for (var i = 0; i < upstreamTemplate.Length; i++) for (var i = 0; i < upstreamTemplate.Length; i++)
@ -138,9 +140,11 @@ namespace Ocelot.Configuration.Creator
upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything); upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
} }
return reRoute.ReRouteIsCaseSensitive var route = reRoute.ReRouteIsCaseSensitive
? $"{upstreamTemplate}{RegExMatchEndString}" ? $"{upstreamTemplate}{RegExMatchEndString}"
: $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}"; : $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
return route;
} }
private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded) private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)

View File

@ -5,6 +5,7 @@ using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Infrastructure.RequestData; using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using Ocelot.Utilities;
namespace Ocelot.DownstreamRouteFinder.Middleware namespace Ocelot.DownstreamRouteFinder.Middleware
{ {
@ -29,7 +30,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
{ {
_logger.LogDebug("started calling downstream route finder middleware"); _logger.LogDebug("started calling downstream route finder middleware");
var upstreamUrlPath = context.Request.Path.ToString(); var upstreamUrlPath = context.Request.Path.ToString().SetLastCharacterAs('/');
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath); _logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);

View File

@ -1,4 +1,5 @@
using System; using System;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -46,14 +47,16 @@ namespace Ocelot.Errors.Middleware
_logger.LogDebug("ocelot pipeline finished"); _logger.LogDebug("ocelot pipeline finished");
} }
private static async Task SetInternalServerErrorOnResponse(HttpContext context) private async Task SetInternalServerErrorOnResponse(HttpContext context)
{ {
context.Response.StatusCode = 500; context.Response.OnStarting(x =>
context.Response.ContentType = "application/json"; {
await context.Response.WriteAsync("Internal Server Error"); context.Response.StatusCode = 500;
return Task.CompletedTask;
}, context);
} }
private static string CreateMessage(HttpContext context, Exception e) private string CreateMessage(HttpContext context, Exception e)
{ {
var message = var message =
$"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}"; $"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";

View File

@ -0,0 +1,17 @@
namespace Ocelot.Utilities
{
public static class StringExtensions
{
public static string SetLastCharacterAs(this string valueToSetLastChar,
char expectedLastChar)
{
var last = valueToSetLastChar[valueToSetLastChar.Length - 1];
if (last != expectedLastChar)
{
valueToSetLastChar = $"{valueToSetLastChar}{expectedLastChar}";
}
return valueToSetLastChar;
}
}
}

View File

@ -56,6 +56,80 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_not_care_about_no_trailing()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamTemplate = "http://localhost:51879/products",
UpstreamTemplate = "/products/",
UpstreamHttpMethod = "Get",
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_not_care_about_trailing()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamTemplate = "http://localhost:51879/products",
UpstreamTemplate = "/products",
UpstreamHttpMethod = "Get",
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_return_not_found()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamTemplate = "http://localhost:51879/products",
UpstreamTemplate = "/products/{productId}",
UpstreamHttpMethod = "Get",
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
.BDDfy();
}
[Fact] [Fact]
public void should_return_response_200_with_complex_url() public void should_return_response_200_with_complex_url()
{ {

View File

@ -77,6 +77,72 @@
"DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}", "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}",
"UpstreamTemplate": "/posts/{postId}", "UpstreamTemplate": "/posts/{postId}",
"UpstreamHttpMethod": "Delete" "UpstreamHttpMethod": "Delete"
},
{
"DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products",
"UpstreamTemplate": "/products",
"UpstreamHttpMethod": "Get",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products/{productId}",
"UpstreamTemplate": "/products/{productId}",
"UpstreamHttpMethod": "Get",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products",
"UpstreamTemplate": "/products",
"UpstreamHttpMethod": "Post",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products/{productId}",
"UpstreamTemplate": "/products/{productId}",
"UpstreamHttpMethod": "Put",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://products20161126090340.azurewebsites.net/api/products/{productId}",
"UpstreamTemplate": "/products/{productId}",
"UpstreamHttpMethod": "Delete",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers",
"UpstreamTemplate": "/customers",
"UpstreamHttpMethod": "Get",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers/{customerId}",
"UpstreamTemplate": "/customers/{customerId}",
"UpstreamHttpMethod": "Get",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers",
"UpstreamTemplate": "/customers",
"UpstreamHttpMethod": "Post",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers/{customerId}",
"UpstreamTemplate": "/customers/{customerId}",
"UpstreamHttpMethod": "Put",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://customers20161126090811.azurewebsites.net/api/customers/{customerId}",
"UpstreamTemplate": "/customers/{customerId}",
"UpstreamHttpMethod": "Delete",
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
"DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts",
"UpstreamTemplate": "/posts/",
"UpstreamHttpMethod": "Get",
"FileCacheOptions": { "TtlSeconds": 15 }
} }
], ],
"GlobalConfiguration": { "GlobalConfiguration": {

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<middlewareConfiguration> <configuration>
<!-- <!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380 Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
@ -11,4 +11,4 @@
</handlers> </handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/> <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
</system.webServer> </system.webServer>
</middlewareConfiguration> </configuration>

View File

@ -59,7 +59,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamTemplate("/products/{productId}") .WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("(?i)/api/products/.*$") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
.Build() .Build()
})) }))
.BDDfy(); .BDDfy();
@ -88,7 +88,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamTemplate("/products/{productId}") .WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("(?i)/api/products/.*$") .WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
.Build() .Build()
})) }))
.BDDfy(); .BDDfy();
@ -118,7 +118,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamTemplate("/products/{productId}") .WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/api/products/.*$") .WithUpstreamTemplatePattern("/api/products/.*/$")
.Build() .Build()
})) }))
.BDDfy(); .BDDfy();
@ -152,7 +152,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamTemplate("/products/{productId}") .WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/api/products/.*$") .WithUpstreamTemplatePattern("/api/products/.*/$")
.WithRequestIdKey("blahhhh") .WithRequestIdKey("blahhhh")
.Build() .Build()
})) }))
@ -183,7 +183,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamTemplate("/products/{productId}") .WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/api/products/.*$") .WithUpstreamTemplatePattern("/api/products/.*/$")
.Build() .Build()
})) }))
.BDDfy(); .BDDfy();
@ -198,7 +198,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamTemplate("/products/{productId}") .WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/api/products/.*$") .WithUpstreamTemplatePattern("/api/products/.*/$")
.WithAuthenticationProvider("IdentityServer") .WithAuthenticationProvider("IdentityServer")
.WithAuthenticationProviderUrl("http://localhost:51888") .WithAuthenticationProviderUrl("http://localhost:51888")
.WithRequireHttps(false) .WithRequireHttps(false)
@ -261,7 +261,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamTemplate("/products/{productId}") .WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}") .WithUpstreamTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/api/products/.*$") .WithUpstreamTemplatePattern("/api/products/.*/$")
.WithAuthenticationProvider("IdentityServer") .WithAuthenticationProvider("IdentityServer")
.WithAuthenticationProviderUrl("http://localhost:51888") .WithAuthenticationProviderUrl("http://localhost:51888")
.WithRequireHttps(false) .WithRequireHttps(false)
@ -323,7 +323,7 @@ namespace Ocelot.UnitTests.Configuration
.WithDownstreamTemplate("/products/{productId}") .WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}") .WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}")
.WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/api/products/.*/variants/.*$") .WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$")
.Build() .Build()
})) }))
.BDDfy(); .BDDfy();

View File

@ -148,7 +148,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
.BDDfy(); .BDDfy();
} }
private void GivenIHaveAUpstreamPath(string downstreamPath) private void GivenIHaveAUpstreamPath(string downstreamPath)
{ {
_downstreamUrlPath = downstreamPath; _downstreamUrlPath = downstreamPath;