bunch of refactoring

This commit is contained in:
TomPallister
2016-10-26 21:38:44 +01:00
parent 9575adc90d
commit 367fa327b3
35 changed files with 264 additions and 157 deletions

View File

@ -54,8 +54,8 @@ namespace Ocelot.DependencyInjection
services.AddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
services.AddSingleton<IClaimsParser, ClaimsParser>();
services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
services.AddSingleton<ITemplateVariableNameAndValueFinder, TemplateVariableNameAndValueFinder>();
services.AddSingleton<IDownstreamUrlTemplateVariableReplacer, DownstreamUrlTemplateVariableReplacer>();
services.AddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
services.AddSingleton<IDownstreamUrlPathPlaceholderReplacer, DownstreamUrlPathPlaceholderReplacer>();
services.AddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
services.AddSingleton<IHttpRequester, HttpClientHttpRequester>();
services.AddSingleton<IHttpResponder, HttpContextResponder>();

View File

@ -6,12 +6,12 @@ namespace Ocelot.DownstreamRouteFinder
{
public class DownstreamRoute
{
public DownstreamRoute(List<TemplateVariableNameAndValue> templateVariableNameAndValues, ReRoute reRoute)
public DownstreamRoute(List<UrlPathPlaceholderNameAndValue> templatePlaceholderNameAndValues, ReRoute reRoute)
{
TemplateVariableNameAndValues = templateVariableNameAndValues;
TemplatePlaceholderNameAndValues = templatePlaceholderNameAndValues;
ReRoute = reRoute;
}
public List<TemplateVariableNameAndValue> TemplateVariableNameAndValues { get; private set; }
public List<UrlPathPlaceholderNameAndValue> TemplatePlaceholderNameAndValues { get; private set; }
public ReRoute ReRoute { get; private set; }
}
}

View File

@ -12,29 +12,28 @@ namespace Ocelot.DownstreamRouteFinder.Finder
{
private readonly IOcelotConfigurationProvider _configProvider;
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
private readonly ITemplateVariableNameAndValueFinder _templateVariableNameAndValueFinder;
private readonly IUrlPathPlaceholderNameAndValueFinder _urlPathPlaceholderNameAndValueFinder;
public DownstreamRouteFinder(IOcelotConfigurationProvider configProvider, IUrlPathToUrlTemplateMatcher urlMatcher, ITemplateVariableNameAndValueFinder templateVariableNameAndValueFinder)
public DownstreamRouteFinder(IOcelotConfigurationProvider configProvider, IUrlPathToUrlTemplateMatcher urlMatcher, IUrlPathPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder)
{
_configProvider = configProvider;
_urlMatcher = urlMatcher;
_templateVariableNameAndValueFinder = templateVariableNameAndValueFinder;
_urlPathPlaceholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
}
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
{
var configuration = _configProvider.Get();
foreach (var template in configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase)))
foreach (var reRoute in configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase)))
{
var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplatePattern);
var urlMatch = _urlMatcher.Match(upstreamUrlPath, reRoute.UpstreamTemplatePattern);
if (urlMatch.Data.Match)
{
var templateVariableNameAndValues = _templateVariableNameAndValueFinder.Find(upstreamUrlPath,
template.UpstreamTemplate);
var templateVariableNameAndValues = _urlPathPlaceholderNameAndValueFinder.Find(upstreamUrlPath, reRoute.UpstreamTemplate);
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, template));
return new OkResponse<DownstreamRoute>(new DownstreamRoute(templateVariableNameAndValues.Data, reRoute));
}
}

View File

@ -1,10 +0,0 @@
using System.Collections.Generic;
using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
{
public interface ITemplateVariableNameAndValueFinder
{
Response<List<TemplateVariableNameAndValue>> Find(string upstreamUrlPath, string upstreamUrlPathTemplate);
}
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
{
public interface IUrlPathPlaceholderNameAndValueFinder
{
Response<List<UrlPathPlaceholderNameAndValue>> Find(string upstreamUrlPath, string upstreamUrlPathTemplate);
}
}

View File

@ -1,8 +1,8 @@
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
{
public class TemplateVariableNameAndValue
public class UrlPathPlaceholderNameAndValue
{
public TemplateVariableNameAndValue(string templateVariableName, string templateVariableValue)
public UrlPathPlaceholderNameAndValue(string templateVariableName, string templateVariableValue)
{
TemplateVariableName = templateVariableName;
TemplateVariableValue = templateVariableValue;

View File

@ -3,11 +3,11 @@ using Ocelot.Responses;
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
{
public class TemplateVariableNameAndValueFinder : ITemplateVariableNameAndValueFinder
public class UrlPathPlaceholderNameAndValueFinder : IUrlPathPlaceholderNameAndValueFinder
{
public Response<List<TemplateVariableNameAndValue>> Find(string upstreamUrlPath, string upstreamUrlPathTemplate)
public Response<List<UrlPathPlaceholderNameAndValue>> Find(string upstreamUrlPath, string upstreamUrlPathTemplate)
{
var templateKeysAndValues = new List<TemplateVariableNameAndValue>();
var templateKeysAndValues = new List<UrlPathPlaceholderNameAndValue>();
int counterForUrl = 0;
@ -21,7 +21,7 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
var variableValue = GetPlaceholderVariableValue(upstreamUrlPath, counterForUrl);
var templateVariableNameAndValue = new TemplateVariableNameAndValue(variableName, variableValue);
var templateVariableNameAndValue = new UrlPathPlaceholderNameAndValue(variableName, variableValue);
templateKeysAndValues.Add(templateVariableNameAndValue);
@ -32,12 +32,12 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher
continue;
}
return new OkResponse<List<TemplateVariableNameAndValue>>(templateKeysAndValues);
return new OkResponse<List<UrlPathPlaceholderNameAndValue>>(templateKeysAndValues);
}
counterForUrl++;
}
return new OkResponse<List<TemplateVariableNameAndValue>>(templateKeysAndValues);
return new OkResponse<List<UrlPathPlaceholderNameAndValue>>(templateKeysAndValues);
}
private string GetPlaceholderVariableValue(string urlPath, int counterForUrl)

View File

@ -10,11 +10,11 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
public class DownstreamUrlCreatorMiddleware : OcelotMiddleware
{
private readonly RequestDelegate _next;
private readonly IDownstreamUrlTemplateVariableReplacer _urlReplacer;
private readonly IDownstreamUrlPathPlaceholderReplacer _urlReplacer;
private readonly IRequestScopedDataRepository _requestScopedDataRepository;
public DownstreamUrlCreatorMiddleware(RequestDelegate next,
IDownstreamUrlTemplateVariableReplacer urlReplacer,
IDownstreamUrlPathPlaceholderReplacer urlReplacer,
IRequestScopedDataRepository requestScopedDataRepository)
:base(requestScopedDataRepository)
{
@ -33,7 +33,7 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
return;
}
var downstreamUrl = _urlReplacer.ReplaceTemplateVariables(downstreamRoute.Data);
var downstreamUrl = _urlReplacer.Replace(downstreamRoute.Data.ReRoute.DownstreamTemplate, downstreamRoute.Data.TemplatePlaceholderNameAndValues);
if (downstreamUrl.IsError)
{
@ -41,7 +41,7 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
return;
}
_requestScopedDataRepository.Add("DownstreamUrl", downstreamUrl.Data);
_requestScopedDataRepository.Add("DownstreamUrl", downstreamUrl.Data.Value);
await _next.Invoke(context);
}

View File

@ -0,0 +1,12 @@
namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
{
public class DownstreamUrl
{
public DownstreamUrl(string value)
{
Value = value;
}
public string Value { get; private set; }
}
}

View File

@ -1,23 +1,25 @@
using System.Collections.Generic;
using System.Text;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Responses;
namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
{
public class DownstreamUrlTemplateVariableReplacer : IDownstreamUrlTemplateVariableReplacer
public class DownstreamUrlPathPlaceholderReplacer : IDownstreamUrlPathPlaceholderReplacer
{
public Response<string> ReplaceTemplateVariables(DownstreamRoute downstreamRoute)
public Response<DownstreamUrl> Replace(string downstreamTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
{
var upstreamUrl = new StringBuilder();
upstreamUrl.Append(downstreamRoute.ReRoute.DownstreamTemplate);
upstreamUrl.Append(downstreamTemplate);
foreach (var templateVarAndValue in downstreamRoute.TemplateVariableNameAndValues)
foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues)
{
upstreamUrl.Replace(templateVarAndValue.TemplateVariableName, templateVarAndValue.TemplateVariableValue);
upstreamUrl.Replace(placeholderVariableAndValue.TemplateVariableName, placeholderVariableAndValue.TemplateVariableValue);
}
return new OkResponse<string>(upstreamUrl.ToString());
return new OkResponse<DownstreamUrl>(new DownstreamUrl(upstreamUrl.ToString()));
}
}
}

View File

@ -1,10 +1,11 @@
using Ocelot.DownstreamRouteFinder;
using System.Collections.Generic;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Responses;
namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
{
public interface IDownstreamUrlTemplateVariableReplacer
public interface IDownstreamUrlPathPlaceholderReplacer
{
Response<string> ReplaceTemplateVariables(DownstreamRoute downstreamRoute);
Response<DownstreamUrl> Replace(string downstreamTemplate, List<UrlPathPlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Ocelot.Middleware
{
public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public ExceptionHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
}
public async Task Invoke(HttpContext context)
{
try
{
await _next.Invoke(context);
}
catch (Exception e)
{
_logger.LogError(new EventId(1, "global error"), "Exception caught in global error handler", e);
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("Internal Server Error");
}
}
}
}

View File

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Builder;
namespace Ocelot.Middleware
{
public static class ExceptionHandlerMiddlewareExtensions
{
public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ExceptionHandlerMiddleware>();
}
}
}

View File

@ -19,22 +19,34 @@ namespace Ocelot.Middleware
{
public static IApplicationBuilder UseOcelot(this IApplicationBuilder builder)
{
builder.UseHttpResponderMiddleware();
// This is registered to catch any global exceptions that are not handled
builder.UseExceptionHandlerMiddleware();
// This is registered first so it can catch any errors and issue an appropriate response
builder.UseHttpErrorResponderMiddleware();
// Then we get the downstream route information
builder.UseDownstreamRouteFinderMiddleware();
// Now we know where the client is going to go we can authenticate them
builder.UseAuthenticationMiddleware();
// The next thing we do is look at any claims transforms in case this is important for authorisation
builder.UseClaimsBuilderMiddleware();
// Now we have authenticated and done any claims transformation we can authorise the request
builder.UseAuthorisationMiddleware();
// Now we can run any header transformation logic
builder.UseHttpRequestHeadersBuilderMiddleware();
// This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
builder.UseDownstreamUrlCreatorMiddleware();
// Everything should now be ready to build or HttpRequest
builder.UseHttpRequestBuilderMiddleware();
//We fire off the request and set the response on the context in this middleware
builder.UseHttpRequesterMiddleware();
return builder;
@ -44,7 +56,7 @@ namespace Ocelot.Middleware
{
builder.UseIfNotNull(middlewareConfiguration.PreHttpResponderMiddleware);
builder.UseHttpResponderMiddleware();
builder.UseHttpErrorResponderMiddleware();
builder.UseIfNotNull(middlewareConfiguration.PostHttpResponderMiddleware);

View File

@ -22,18 +22,13 @@ namespace Ocelot.RequestBuilder.Builder
if (content != null)
{
using (var reader = new StreamReader(content))
{
var body = await reader.ReadToEndAsync();
httpRequestMessage.Content = new StringContent(body);
}
httpRequestMessage.Content = new StreamContent(content);
}
if (!string.IsNullOrEmpty(contentType))
{
var splitCt = contentType.Split(';');
var cT = splitCt[0];
httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue(cT);
httpRequestMessage.Content.Headers.Remove("Content-Type");
httpRequestMessage.Content.Headers.TryAddWithoutValidation("Content-Type", contentType);
}
//todo get rid of if
@ -50,7 +45,7 @@ namespace Ocelot.RequestBuilder.Builder
//todo get rid of if..
if (header.Key.ToLower() != "host")
{
httpRequestMessage.Headers.Add(header.Key, header.Value.ToArray());
httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
}

View File

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Middleware;
using Ocelot.RequestBuilder;
using Ocelot.Responder;
namespace Ocelot.Requester.Middleware
{
@ -11,15 +12,18 @@ namespace Ocelot.Requester.Middleware
private readonly RequestDelegate _next;
private readonly IHttpRequester _requester;
private readonly IRequestScopedDataRepository _requestScopedDataRepository;
private readonly IHttpResponder _responder;
public HttpRequesterMiddleware(RequestDelegate next,
IHttpRequester requester,
IRequestScopedDataRepository requestScopedDataRepository)
IRequestScopedDataRepository requestScopedDataRepository,
IHttpResponder responder)
:base(requestScopedDataRepository)
{
_next = next;
_requester = requester;
_requestScopedDataRepository = requestScopedDataRepository;
_responder = responder;
}
public async Task Invoke(HttpContext context)
@ -40,7 +44,12 @@ namespace Ocelot.Requester.Middleware
return;
}
_requestScopedDataRepository.Add("Response", response.Data);
var setResponse = await _responder.SetResponseOnHttpContext(context, response.Data);
if (setResponse.IsError)
{
SetPipelineError(response.Errors);
}
}
}
}

View File

@ -1,6 +1,7 @@
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Responses;
namespace Ocelot.Responder
{
@ -10,7 +11,7 @@ namespace Ocelot.Responder
/// </summary>
public class HttpContextResponder : IHttpResponder
{
public async Task<HttpContext> CreateResponse(HttpContext context, HttpResponseMessage response)
public async Task<Response> SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response)
{
context.Response.OnStarting(x =>
{
@ -19,17 +20,17 @@ namespace Ocelot.Responder
}, context);
await context.Response.WriteAsync(await response.Content.ReadAsStringAsync());
return context;
return new OkResponse();
}
public async Task<HttpContext> CreateErrorResponse(HttpContext context, int statusCode)
public async Task<Response> SetErrorResponseOnContext(HttpContext context, int statusCode)
{
context.Response.OnStarting(x =>
{
context.Response.StatusCode = statusCode;
return Task.CompletedTask;
}, context);
return context;
return new OkResponse();
}
}
}

View File

@ -1,12 +1,13 @@
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Responses;
namespace Ocelot.Responder
{
public interface IHttpResponder
{
Task<HttpContext> CreateResponse(HttpContext context, HttpResponseMessage response);
Task<HttpContext> CreateErrorResponse(HttpContext context, int statusCode);
Task<Response> SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response);
Task<Response> SetErrorResponseOnContext(HttpContext context, int statusCode);
}
}

View File

@ -6,14 +6,14 @@ using Ocelot.Middleware;
namespace Ocelot.Responder.Middleware
{
public class HttpResponderMiddleware : OcelotMiddleware
public class HttpErrorResponderMiddleware : OcelotMiddleware
{
private readonly RequestDelegate _next;
private readonly IHttpResponder _responder;
private readonly IRequestScopedDataRepository _requestScopedDataRepository;
private readonly IErrorsToHttpStatusCodeMapper _codeMapper;
public HttpResponderMiddleware(RequestDelegate next,
public HttpErrorResponderMiddleware(RequestDelegate next,
IHttpResponder responder,
IRequestScopedDataRepository requestScopedDataRepository,
IErrorsToHttpStatusCodeMapper codeMapper)
@ -37,19 +37,13 @@ namespace Ocelot.Responder.Middleware
if (!statusCode.IsError)
{
await _responder.CreateErrorResponse(context, statusCode.Data);
await _responder.SetErrorResponseOnContext(context, statusCode.Data);
}
else
{
await _responder.CreateErrorResponse(context, 500);
await _responder.SetErrorResponseOnContext(context, 500);
}
}
else
{
var response = _requestScopedDataRepository.Get<HttpResponseMessage>("Response");
await _responder.CreateResponse(context, response.Data);
}
}
}
}

View File

@ -4,9 +4,9 @@ namespace Ocelot.Responder.Middleware
{
public static class HttpResponderMiddlewareExtensions
{
public static IApplicationBuilder UseHttpResponderMiddleware(this IApplicationBuilder builder)
public static IApplicationBuilder UseHttpErrorResponderMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<HttpResponderMiddleware>();
return builder.UseMiddleware<HttpErrorResponderMiddleware>();
}
}
}