some tidying up and a bit of refactoring...not too happy about how the proxy is working at the moment! May need a rethink!

This commit is contained in:
TomPallister 2016-09-11 21:32:56 +01:00
parent 87702141e2
commit 72cec38c0e
16 changed files with 151 additions and 60 deletions

View File

@ -1,11 +1,24 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using Ocelot.Library.Infrastructure.Responses;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.Library.Infrastructure.Configuration namespace Ocelot.Library.Infrastructure.Configuration
{ {
public class ConfigurationValidationResult public class ConfigurationValidationResult
{ {
public ConfigurationValidationResult(bool isError)
{
IsError = isError;
Errors = new List<Error>();
}
public ConfigurationValidationResult(bool isError, List<Error> errors)
{
IsError = isError;
Errors = errors;
}
public bool IsError { get; private set; }
public List<Error> Errors { get; private set; }
} }
} }

View File

@ -1,7 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
namespace Ocelot.Library.Infrastructure.Configuration namespace Ocelot.Library.Infrastructure.Configuration
@ -19,7 +17,7 @@ namespace Ocelot.Library.Infrastructure.Configuration
if (duplicateUpstreamTemplates.Count <= 0) if (duplicateUpstreamTemplates.Count <= 0)
{ {
return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult()); return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false));
} }
var errors = new List<Error>(); var errors = new List<Error>();
@ -31,7 +29,7 @@ namespace Ocelot.Library.Infrastructure.Configuration
errors.Add(error); errors.Add(error);
} }
return new ErrorResponse<ConfigurationValidationResult>(errors); return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(true, errors));
} }
} }
} }

View File

@ -18,7 +18,6 @@ namespace Ocelot.Library.Infrastructure.DownstreamRouteFinder
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath) public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath)
{ {
foreach (var template in _configuration.Value.ReRoutes) foreach (var template in _configuration.Value.ReRoutes)
{ {
var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplate); var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplate);

View File

@ -0,0 +1,21 @@
using System.Net.Http;
using System.Threading.Tasks;
namespace Ocelot.Library.Infrastructure.Requester
{
public class HttpClientHttpRequester : IHttpRequester
{
public async Task<HttpResponseMessage> GetResponse(string httpMethod, string downstreamUrl)
{
var method = new HttpMethod(httpMethod);
var httpRequestMessage = new HttpRequestMessage(method, downstreamUrl);
using (var httpClient = new HttpClient())
{
var response = await httpClient.SendAsync(httpRequestMessage);
return response;
}
}
}
}

View File

@ -0,0 +1,11 @@
using System.Net.Http;
using System.Threading.Tasks;
using Flurl.Http;
namespace Ocelot.Library.Infrastructure.Requester
{
public interface IHttpRequester
{
Task<HttpResponseMessage> GetResponse(string httpMethod, string downstreamUrl);
}
}

View File

@ -0,0 +1,27 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Ocelot.Library.Infrastructure.Responder
{
public class HttpContextResponder : IHttpResponder
{
public async Task<HttpContext> CreateSuccessResponse(HttpContext context, HttpResponseMessage response)
{
if (!response.IsSuccessStatusCode)
{
context.Response.StatusCode = (int)response.StatusCode;
return context;
}
await context.Response.WriteAsync(await response.Content.ReadAsStringAsync());
return context;
}
public async Task<HttpContext> CreateNotFoundResponse(HttpContext context)
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
return context;
}
}
}

View File

@ -0,0 +1,13 @@
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Ocelot.Library.Infrastructure.Responder
{
public interface IHttpResponder
{
Task<HttpContext> CreateSuccessResponse(HttpContext context, HttpResponseMessage response);
Task<HttpContext> CreateNotFoundResponse(HttpContext context);
}
}

View File

@ -5,7 +5,7 @@ namespace Ocelot.Library.Infrastructure.UrlTemplateReplacer
{ {
public class DownstreamUrlTemplateVariableReplacer : IDownstreamUrlTemplateVariableReplacer public class DownstreamUrlTemplateVariableReplacer : IDownstreamUrlTemplateVariableReplacer
{ {
public string ReplaceTemplateVariable(DownstreamRoute downstreamRoute) public string ReplaceTemplateVariables(DownstreamRoute downstreamRoute)
{ {
var upstreamUrl = new StringBuilder(); var upstreamUrl = new StringBuilder();

View File

@ -5,6 +5,6 @@ namespace Ocelot.Library.Infrastructure.UrlTemplateReplacer
{ {
public interface IDownstreamUrlTemplateVariableReplacer public interface IDownstreamUrlTemplateVariableReplacer
{ {
string ReplaceTemplateVariable(DownstreamRoute downstreamRoute); string ReplaceTemplateVariables(DownstreamRoute downstreamRoute);
} }
} }

View File

@ -1,10 +1,10 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Ocelot.Library.Infrastructure.Configuration; using Ocelot.Library.Infrastructure.Configuration;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder; using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Requester;
using Ocelot.Library.Infrastructure.Responder;
using Ocelot.Library.Infrastructure.UrlTemplateReplacer; using Ocelot.Library.Infrastructure.UrlTemplateReplacer;
namespace Ocelot.Library.Middleware namespace Ocelot.Library.Middleware
@ -15,16 +15,22 @@ namespace Ocelot.Library.Middleware
private readonly IDownstreamUrlTemplateVariableReplacer _urlReplacer; private readonly IDownstreamUrlTemplateVariableReplacer _urlReplacer;
private readonly IOptions<Configuration> _configuration; private readonly IOptions<Configuration> _configuration;
private readonly IDownstreamRouteFinder _downstreamRouteFinder; private readonly IDownstreamRouteFinder _downstreamRouteFinder;
private readonly IHttpRequester _requester;
private readonly IHttpResponder _responder;
public ProxyMiddleware(RequestDelegate next, public ProxyMiddleware(RequestDelegate next,
IDownstreamUrlTemplateVariableReplacer urlReplacer, IDownstreamUrlTemplateVariableReplacer urlReplacer,
IOptions<Configuration> configuration, IOptions<Configuration> configuration,
IDownstreamRouteFinder downstreamRouteFinder) IDownstreamRouteFinder downstreamRouteFinder,
IHttpRequester requester,
IHttpResponder responder)
{ {
_next = next; _next = next;
_urlReplacer = urlReplacer; _urlReplacer = urlReplacer;
_configuration = configuration; _configuration = configuration;
_downstreamRouteFinder = downstreamRouteFinder; _downstreamRouteFinder = downstreamRouteFinder;
_requester = requester;
_responder = responder;
} }
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
@ -35,27 +41,15 @@ namespace Ocelot.Library.Middleware
if (downstreamRoute.IsError) if (downstreamRoute.IsError)
{ {
context.Response.StatusCode = (int)HttpStatusCode.NotFound; await _responder.CreateNotFoundResponse(context);
return; return;
} }
var downstreamUrl = _urlReplacer.ReplaceTemplateVariable(downstreamRoute.Data); var downstreamUrl = _urlReplacer.ReplaceTemplateVariables(downstreamRoute.Data);
using (var httpClient = new HttpClient()) var response = await _requester.GetResponse(context.Request.Method, downstreamUrl);
{
var httpMethod = new HttpMethod(context.Request.Method);
var httpRequestMessage = new HttpRequestMessage(httpMethod, downstreamUrl); context = await _responder.CreateSuccessResponse(context, response);
var response = await httpClient.SendAsync(httpRequestMessage);
if (!response.IsSuccessStatusCode)
{
context.Response.StatusCode = (int)response.StatusCode;
return;
}
await context.Response.WriteAsync(await response.Content.ReadAsStringAsync());
}
await _next.Invoke(context); await _next.Invoke(context);
} }

View File

@ -17,7 +17,8 @@
"Microsoft.Extensions.Logging.Debug": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
"Microsoft.AspNetCore.Http": "1.0.0", "Microsoft.AspNetCore.Http": "1.0.0",
"YamlDotNet": "3.9.0" "YamlDotNet": "3.9.0",
"Flurl.Http": "1.0.1"
}, },
"frameworks": { "frameworks": {

View File

@ -4,6 +4,8 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder; using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Requester;
using Ocelot.Library.Infrastructure.Responder;
using Ocelot.Library.Middleware; using Ocelot.Library.Middleware;
namespace Ocelot namespace Ocelot
@ -38,6 +40,8 @@ namespace Ocelot
services.AddSingleton<IUrlPathToUrlTemplateMatcher, UrlPathToUrlTemplateMatcher>(); services.AddSingleton<IUrlPathToUrlTemplateMatcher, UrlPathToUrlTemplateMatcher>();
services.AddSingleton<IDownstreamUrlTemplateVariableReplacer, DownstreamUrlTemplateVariableReplacer>(); services.AddSingleton<IDownstreamUrlTemplateVariableReplacer, DownstreamUrlTemplateVariableReplacer>();
services.AddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder>(); services.AddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder>();
services.AddSingleton<IHttpRequester, HttpClientHttpRequester>();
services.AddSingleton<IHttpResponder, HttpContextResponder>();
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

View File

@ -20,16 +20,19 @@ namespace Ocelot.AcceptanceTests
private TestServer _server; private TestServer _server;
private HttpClient _client; private HttpClient _client;
private HttpResponseMessage _response; private HttpResponseMessage _response;
private readonly string _configurationPath;
public OcelotTests() public OcelotTests()
{ {
_configurationPath = "./bin/Debug/netcoreapp1.0/configuration.yaml";
_fakeService = new FakeService(); _fakeService = new FakeService();
} }
[Fact] [Fact]
public void should_return_response_404() public void should_return_response_404()
{ {
this.Given(x => x.GivenTheApiGatewayIsRunning()) this.Given(x => x.GivenThereIsAConfiguration(new Configuration()))
.And(x => x.GivenTheApiGatewayIsRunning())
.When(x => x.WhenIRequestTheUrlOnTheApiGateway("/")) .When(x => x.WhenIRequestTheUrlOnTheApiGateway("/"))
.Then(x => x.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) .Then(x => x.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
.BDDfy(); .BDDfy();
@ -72,12 +75,12 @@ namespace Ocelot.AcceptanceTests
{ {
var serializer = new Serializer(); var serializer = new Serializer();
if (File.Exists("./configuration.yaml")) if (File.Exists(_configurationPath))
{ {
File.Delete("./configuration.yaml"); File.Delete(_configurationPath);
} }
using (TextWriter writer = File.CreateText("./configuration.yaml")) using (TextWriter writer = File.CreateText(_configurationPath))
{ {
serializer.Serialize(writer, configuration); serializer.Serialize(writer, configuration);
} }

View File

@ -58,6 +58,7 @@ namespace Ocelot.UnitTests
})) }))
.When(x => x.WhenIValidateTheConfiguration()) .When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid()) .Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorIs<DownstreamTemplateAlreadyUsedError>())
.BDDfy(); .BDDfy();
} }
@ -73,12 +74,17 @@ namespace Ocelot.UnitTests
private void ThenTheResultIsValid() private void ThenTheResultIsValid()
{ {
_result.IsError.ShouldBeFalse(); _result.Data.IsError.ShouldBeFalse();
} }
private void ThenTheResultIsNotValid() private void ThenTheResultIsNotValid()
{ {
_result.IsError.ShouldBeTrue(); _result.Data.IsError.ShouldBeTrue();
}
private void ThenTheErrorIs<T>()
{
_result.Data.Errors[0].ShouldBeOfType<T>();
} }
} }
} }

View File

@ -35,21 +35,21 @@ namespace Ocelot.UnitTests
[Fact] [Fact]
public void should_return_route() public void should_return_route()
{ {
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("somePath")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And(x => x.GivenTheConfigurationIs(new Configuration { .And(x => x.GivenTheConfigurationIs(new Configuration {
ReRoutes = new List<ReRoute> ReRoutes = new List<ReRoute>
{ {
new ReRoute() new ReRoute()
{ {
UpstreamTemplate = "somePath", UpstreamTemplate = "someUpstreamPath",
DownstreamTemplate = "somPath" DownstreamTemplate = "someDownstreamPath"
} }
} }
})) }))
.And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(true, new List<TemplateVariableNameAndValue>(), "somePath"))) .And(x => x.GivenTheUrlMatcherReturns(new UrlMatch(true, new List<TemplateVariableNameAndValue>(), "someDownstreamPath")))
.When(x => x.WhenICallTheFinder()) .When(x => x.WhenICallTheFinder())
.Then( .Then(
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), "somePath"))) x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), "someDownstreamPath")))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
@ -117,6 +117,7 @@ namespace Ocelot.UnitTests
private void ThenTheFollowingIsReturned(DownstreamRoute expected) private void ThenTheFollowingIsReturned(DownstreamRoute expected)
{ {
_result.Data.DownstreamUrlTemplate.ShouldBe(expected.DownstreamUrlTemplate); _result.Data.DownstreamUrlTemplate.ShouldBe(expected.DownstreamUrlTemplate);
for (int i = 0; i < _result.Data.TemplateVariableNameAndValues.Count; i++) for (int i = 0; i < _result.Data.TemplateVariableNameAndValues.Count; i++)
{ {
_result.Data.TemplateVariableNameAndValues[i].TemplateVariableName.ShouldBe( _result.Data.TemplateVariableNameAndValues[i].TemplateVariableName.ShouldBe(

View File

@ -131,7 +131,7 @@ namespace Ocelot.UnitTests
private void WhenIReplaceTheTemplateVariables() private void WhenIReplaceTheTemplateVariables()
{ {
_result = _downstreamUrlPathReplacer.ReplaceTemplateVariable(_downstreamRoute); _result = _downstreamUrlPathReplacer.ReplaceTemplateVariables(_downstreamRoute);
} }
private void ThenTheDownstreamUrlPathIsReturned(string expected) private void ThenTheDownstreamUrlPathIsReturned(string expected)