mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Now supports the same upstream url by difrenciating by http method. Also broke up the proxy middleware into three seperate pieces that do their thing and stick something into the OWIN context
This commit is contained in:
parent
da1957311b
commit
ab8407e7dc
@ -4,5 +4,6 @@
|
|||||||
{
|
{
|
||||||
public string DownstreamTemplate { get; set; }
|
public string DownstreamTemplate { get; set; }
|
||||||
public string UpstreamTemplate { get; set; }
|
public string UpstreamTemplate { get; set; }
|
||||||
|
public string UpstreamHttpMethod { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Ocelot.Library.Infrastructure.Responses;
|
using Ocelot.Library.Infrastructure.Responses;
|
||||||
using Ocelot.Library.Infrastructure.UrlMatcher;
|
using Ocelot.Library.Infrastructure.UrlMatcher;
|
||||||
@ -16,9 +18,9 @@ namespace Ocelot.Library.Infrastructure.DownstreamRouteFinder
|
|||||||
_urlMatcher = urlMatcher;
|
_urlMatcher = urlMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath)
|
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
|
||||||
{
|
{
|
||||||
foreach (var template in _configuration.Value.ReRoutes)
|
foreach (var template in _configuration.Value.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase)))
|
||||||
{
|
{
|
||||||
var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplate);
|
var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplate);
|
||||||
|
|
||||||
|
@ -4,6 +4,6 @@ namespace Ocelot.Library.Infrastructure.DownstreamRouteFinder
|
|||||||
{
|
{
|
||||||
public interface IDownstreamRouteFinder
|
public interface IDownstreamRouteFinder
|
||||||
{
|
{
|
||||||
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath);
|
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
|
||||||
|
using Ocelot.Library.Infrastructure.Responder;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Middleware
|
||||||
|
{
|
||||||
|
public class DownstreamRouteFinderMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
|
||||||
|
private readonly IHttpResponder _responder;
|
||||||
|
|
||||||
|
public DownstreamRouteFinderMiddleware(RequestDelegate next,
|
||||||
|
IDownstreamRouteFinder downstreamRouteFinder,
|
||||||
|
IHttpResponder responder)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_downstreamRouteFinder = downstreamRouteFinder;
|
||||||
|
_responder = responder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
var upstreamUrlPath = context.Request.Path.ToString();
|
||||||
|
|
||||||
|
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method);
|
||||||
|
|
||||||
|
if (downstreamRoute.IsError)
|
||||||
|
{
|
||||||
|
await _responder.CreateNotFoundResponse(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Items.Add("DownstreamRoute", downstreamRoute.Data);
|
||||||
|
|
||||||
|
await _next.Invoke(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Middleware
|
||||||
|
{
|
||||||
|
public static class DownstreamRouteFinderMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseDownstreamRouteFinderMiddleware(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<DownstreamRouteFinderMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
|
||||||
|
using Ocelot.Library.Infrastructure.UrlTemplateReplacer;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Middleware
|
||||||
|
{
|
||||||
|
public class DownstreamUrlCreatorMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly IDownstreamUrlTemplateVariableReplacer _urlReplacer;
|
||||||
|
|
||||||
|
public DownstreamUrlCreatorMiddleware(RequestDelegate next,
|
||||||
|
IDownstreamUrlTemplateVariableReplacer urlReplacer)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_urlReplacer = urlReplacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
var downstreamRoute = GetDownstreamRouteFromOwinItems(context);
|
||||||
|
|
||||||
|
var downstreamUrl = _urlReplacer.ReplaceTemplateVariables(downstreamRoute);
|
||||||
|
|
||||||
|
context.Items.Add("DownstreamUrl", downstreamUrl);
|
||||||
|
|
||||||
|
await _next.Invoke(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DownstreamRoute GetDownstreamRouteFromOwinItems(HttpContext context)
|
||||||
|
{
|
||||||
|
object obj;
|
||||||
|
DownstreamRoute downstreamRoute = null;
|
||||||
|
if (context.Items.TryGetValue("DownstreamRoute", out obj))
|
||||||
|
{
|
||||||
|
downstreamRoute = (DownstreamRoute) obj;
|
||||||
|
}
|
||||||
|
return downstreamRoute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Middleware
|
||||||
|
{
|
||||||
|
public static class DownstreamUrlCreatorMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UserDownstreamUrlCreatorMiddleware(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<DownstreamUrlCreatorMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/Ocelot.Library/Middleware/HttpRequesterMiddleware.cs
Normal file
47
src/Ocelot.Library/Middleware/HttpRequesterMiddleware.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Library.Infrastructure.Requester;
|
||||||
|
using Ocelot.Library.Infrastructure.Responder;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Middleware
|
||||||
|
{
|
||||||
|
public class HttpRequesterMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly IHttpRequester _requester;
|
||||||
|
private readonly IHttpResponder _responder;
|
||||||
|
|
||||||
|
public HttpRequesterMiddleware(RequestDelegate next,
|
||||||
|
IHttpRequester requester,
|
||||||
|
IHttpResponder responder)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_requester = requester;
|
||||||
|
_responder = responder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
var downstreamUrl = GetDownstreamUrlFromOwinItems(context);
|
||||||
|
|
||||||
|
var response = await _requester
|
||||||
|
.GetResponse(context.Request.Method, downstreamUrl, context.Request.Body,
|
||||||
|
context.Request.Headers, context.Request.Cookies, context.Request.Query, context.Request.ContentType);
|
||||||
|
|
||||||
|
await _responder.CreateResponse(context, response);
|
||||||
|
|
||||||
|
await _next.Invoke(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetDownstreamUrlFromOwinItems(HttpContext context)
|
||||||
|
{
|
||||||
|
object obj;
|
||||||
|
string downstreamUrl = null;
|
||||||
|
if (context.Items.TryGetValue("DownstreamUrl", out obj))
|
||||||
|
{
|
||||||
|
downstreamUrl = (string) obj;
|
||||||
|
}
|
||||||
|
return downstreamUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Middleware
|
||||||
|
{
|
||||||
|
public static class HttpRequesterMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseHttpRequesterMiddleware(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<HttpRequesterMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
|
|
||||||
namespace Ocelot.Library.Middleware
|
|
||||||
{
|
|
||||||
public static class ProxyExtensions
|
|
||||||
{
|
|
||||||
public static IApplicationBuilder UseProxy(this IApplicationBuilder builder)
|
|
||||||
{
|
|
||||||
return builder.UseMiddleware<ProxyMiddleware>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Ocelot.Library.Infrastructure.Configuration;
|
|
||||||
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
|
|
||||||
using Ocelot.Library.Infrastructure.Requester;
|
|
||||||
using Ocelot.Library.Infrastructure.Responder;
|
|
||||||
using Ocelot.Library.Infrastructure.UrlTemplateReplacer;
|
|
||||||
|
|
||||||
namespace Ocelot.Library.Middleware
|
|
||||||
{
|
|
||||||
public class ProxyMiddleware
|
|
||||||
{
|
|
||||||
private readonly RequestDelegate _next;
|
|
||||||
private readonly IDownstreamUrlTemplateVariableReplacer _urlReplacer;
|
|
||||||
private readonly IOptions<Configuration> _configuration;
|
|
||||||
private readonly IDownstreamRouteFinder _downstreamRouteFinder;
|
|
||||||
private readonly IHttpRequester _requester;
|
|
||||||
private readonly IHttpResponder _responder;
|
|
||||||
|
|
||||||
public ProxyMiddleware(RequestDelegate next,
|
|
||||||
IDownstreamUrlTemplateVariableReplacer urlReplacer,
|
|
||||||
IOptions<Configuration> configuration,
|
|
||||||
IDownstreamRouteFinder downstreamRouteFinder,
|
|
||||||
IHttpRequester requester,
|
|
||||||
IHttpResponder responder)
|
|
||||||
{
|
|
||||||
_next = next;
|
|
||||||
_urlReplacer = urlReplacer;
|
|
||||||
_configuration = configuration;
|
|
||||||
_downstreamRouteFinder = downstreamRouteFinder;
|
|
||||||
_requester = requester;
|
|
||||||
_responder = responder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
|
||||||
{
|
|
||||||
var upstreamUrlPath = context.Request.Path.ToString();
|
|
||||||
|
|
||||||
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath);
|
|
||||||
|
|
||||||
if (downstreamRoute.IsError)
|
|
||||||
{
|
|
||||||
await _responder.CreateNotFoundResponse(context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var downstreamUrl = _urlReplacer.ReplaceTemplateVariables(downstreamRoute.Data);
|
|
||||||
|
|
||||||
var response = await _requester
|
|
||||||
.GetResponse(context.Request.Method, downstreamUrl, context.Request.Body,
|
|
||||||
context.Request.Headers, context.Request.Cookies, context.Request.Query, context.Request.ContentType);
|
|
||||||
|
|
||||||
await _responder.CreateResponse(context, response);
|
|
||||||
|
|
||||||
await _next.Invoke(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -51,7 +51,11 @@ namespace Ocelot
|
|||||||
|
|
||||||
loggerFactory.AddDebug();
|
loggerFactory.AddDebug();
|
||||||
|
|
||||||
app.UseProxy();
|
app.UseDownstreamRouteFinderMiddleware();
|
||||||
|
|
||||||
|
app.UserDownstreamUrlCreatorMiddleware();
|
||||||
|
|
||||||
|
app.UseHttpRequesterMiddleware();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new ReRoute
|
new ReRoute
|
||||||
{
|
{
|
||||||
DownstreamTemplate = "http://localhost:51879/",
|
DownstreamTemplate = "http://localhost:51879/",
|
||||||
UpstreamTemplate = "/"
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -72,7 +73,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new ReRoute
|
new ReRoute
|
||||||
{
|
{
|
||||||
DownstreamTemplate = "http://localhost:51879/",
|
DownstreamTemplate = "http://localhost:51879/",
|
||||||
UpstreamTemplate = "/"
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Post"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
ReRoutes:
|
ReRoutes:
|
||||||
- DownstreamTemplate: http://localhost:51879/
|
- DownstreamTemplate: http://localhost:51879/
|
||||||
UpstreamTemplate: /
|
UpstreamTemplate: /
|
||||||
|
HttpMethod: Get
|
||||||
|
@ -21,6 +21,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
private Response<DownstreamRoute> _response;
|
private Response<DownstreamRoute> _response;
|
||||||
private Library.Infrastructure.Configuration.Configuration _configuration;
|
private Library.Infrastructure.Configuration.Configuration _configuration;
|
||||||
private UrlMatch _match;
|
private UrlMatch _match;
|
||||||
|
private string _upstreamHttpMethod;
|
||||||
|
|
||||||
public DownstreamRouteFinderTests()
|
public DownstreamRouteFinderTests()
|
||||||
{
|
{
|
||||||
@ -39,11 +40,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
new ReRoute()
|
new ReRoute()
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "someUpstreamPath",
|
UpstreamTemplate = "someUpstreamPath",
|
||||||
DownstreamTemplate = "someDownstreamPath"
|
DownstreamTemplate = "someDownstreamPath",
|
||||||
|
UpstreamHttpMethod = "Get"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(true, new List<TemplateVariableNameAndValue>(), "someDownstreamPath")))
|
.And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(true, new List<TemplateVariableNameAndValue>(), "someDownstreamPath")))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
.When(x => x.WhenICallTheFinder())
|
.When(x => x.WhenICallTheFinder())
|
||||||
.Then(
|
.Then(
|
||||||
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), "someDownstreamPath")))
|
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), "someDownstreamPath")))
|
||||||
@ -51,6 +54,36 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_correct_route_for_http_verb()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
|
||||||
|
.And(x => x.GivenTheConfigurationIs(new Library.Infrastructure.Configuration.Configuration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<ReRoute>
|
||||||
|
{
|
||||||
|
new ReRoute()
|
||||||
|
{
|
||||||
|
UpstreamTemplate = "someUpstreamPath",
|
||||||
|
DownstreamTemplate = "someDownstreamPath",
|
||||||
|
UpstreamHttpMethod = "Get"
|
||||||
|
},
|
||||||
|
new ReRoute()
|
||||||
|
{
|
||||||
|
UpstreamTemplate = "someUpstreamPath",
|
||||||
|
DownstreamTemplate = "someDownstreamPathForAPost",
|
||||||
|
UpstreamHttpMethod = "Post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(true, new List<TemplateVariableNameAndValue>(), "someDownstreamPathForAPost")))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
|
||||||
|
.When(x => x.WhenICallTheFinder())
|
||||||
|
.Then(
|
||||||
|
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), "someDownstreamPathForAPost")))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_not_return_route()
|
public void should_not_return_route()
|
||||||
{
|
{
|
||||||
@ -62,11 +95,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
new ReRoute()
|
new ReRoute()
|
||||||
{
|
{
|
||||||
UpstreamTemplate = "somePath",
|
UpstreamTemplate = "somePath",
|
||||||
DownstreamTemplate = "somPath"
|
DownstreamTemplate = "somPath",
|
||||||
|
UpstreamHttpMethod = "Get"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(false, new List<TemplateVariableNameAndValue>(), null)))
|
.And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(false, new List<TemplateVariableNameAndValue>(), null)))
|
||||||
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
.When(x => x.WhenICallTheFinder())
|
.When(x => x.WhenICallTheFinder())
|
||||||
.Then(
|
.Then(
|
||||||
x => x.ThenAnErrorResponseIsReturned())
|
x => x.ThenAnErrorResponseIsReturned())
|
||||||
@ -74,6 +109,11 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GivenTheUpstreamHttpMethodIs(string upstreamHttpMethod)
|
||||||
|
{
|
||||||
|
_upstreamHttpMethod = upstreamHttpMethod;
|
||||||
|
}
|
||||||
|
|
||||||
private void ThenAnErrorResponseIsReturned()
|
private void ThenAnErrorResponseIsReturned()
|
||||||
{
|
{
|
||||||
_result.IsError.ShouldBeTrue();
|
_result.IsError.ShouldBeTrue();
|
||||||
@ -108,7 +148,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
|
|
||||||
private void WhenICallTheFinder()
|
private void WhenICallTheFinder()
|
||||||
{
|
{
|
||||||
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath);
|
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
"Shouldly": "2.8.0",
|
"Shouldly": "2.8.0",
|
||||||
"TestStack.BDDfy": "4.3.1",
|
"TestStack.BDDfy": "4.3.1",
|
||||||
"YamlDotNet": "3.9.0",
|
"YamlDotNet": "3.9.0",
|
||||||
"Moq": "4.6.38-alpha"
|
"Moq": "4.6.38-alpha",
|
||||||
|
"Microsoft.AspNetCore.TestHost": "1.0.0"
|
||||||
},
|
},
|
||||||
|
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user