mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 09:38:14 +08:00
refactoring configuration code so its not so crazy, still need to work on the creator class
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Library.Configuration;
|
||||
|
||||
namespace Ocelot.Library.Builder
|
||||
namespace Ocelot.Library.Configuration.Builder
|
||||
{
|
||||
public class ReRouteBuilder
|
||||
{
|
@ -0,0 +1,9 @@
|
||||
using Ocelot.Library.Responses;
|
||||
|
||||
namespace Ocelot.Library.Configuration.Creator
|
||||
{
|
||||
public interface IOcelotConfigurationCreator
|
||||
{
|
||||
Response<IOcelotConfiguration> Create();
|
||||
}
|
||||
}
|
@ -1,127 +1,14 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.Library.RequestBuilder;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ocelot.Library.Configuration
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yaml;
|
||||
|
||||
public class OcelotConfiguration : IOcelotConfiguration
|
||||
{
|
||||
private readonly IOptions<YamlConfiguration> _options;
|
||||
private readonly IConfigurationValidator _configurationValidator;
|
||||
private readonly List<ReRoute> _reRoutes;
|
||||
private const string RegExMatchEverything = ".*";
|
||||
private const string RegExMatchEndString = "$";
|
||||
private readonly IClaimToHeaderConfigurationParser _claimToHeaderConfigurationParser;
|
||||
private readonly ILogger<OcelotConfiguration> _logger;
|
||||
|
||||
public OcelotConfiguration(IOptions<YamlConfiguration> options,
|
||||
IConfigurationValidator configurationValidator,
|
||||
IClaimToHeaderConfigurationParser claimToHeaderConfigurationParser,
|
||||
ILogger<OcelotConfiguration> logger)
|
||||
public OcelotConfiguration(List<ReRoute> reRoutes)
|
||||
{
|
||||
_options = options;
|
||||
_configurationValidator = configurationValidator;
|
||||
_claimToHeaderConfigurationParser = claimToHeaderConfigurationParser;
|
||||
_logger = logger;
|
||||
_reRoutes = new List<ReRoute>();
|
||||
SetUpConfiguration();
|
||||
ReRoutes = reRoutes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is meant to be tempoary to convert a yaml config to an ocelot config...probably wont keep this but we will see
|
||||
/// will need a refactor at some point as its crap
|
||||
/// </summary>
|
||||
private void SetUpConfiguration()
|
||||
{
|
||||
var response = _configurationValidator.IsValid(_options.Value);
|
||||
|
||||
if (!response.IsError && !response.Data.IsError)
|
||||
{
|
||||
foreach (var reRoute in _options.Value.ReRoutes)
|
||||
{
|
||||
SetUpReRoute(reRoute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetUpReRoute(YamlReRoute reRoute)
|
||||
{
|
||||
var upstreamTemplate = reRoute.UpstreamTemplate;
|
||||
|
||||
var placeholders = new List<string>();
|
||||
|
||||
for (var i = 0; i < upstreamTemplate.Length; i++)
|
||||
{
|
||||
if (IsPlaceHolder(upstreamTemplate, i))
|
||||
{
|
||||
var postitionOfPlaceHolderClosingBracket = upstreamTemplate.IndexOf('}', i);
|
||||
var difference = postitionOfPlaceHolderClosingBracket - i + 1;
|
||||
var variableName = upstreamTemplate.Substring(i, difference);
|
||||
placeholders.Add(variableName);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var placeholder in placeholders)
|
||||
{
|
||||
upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
|
||||
}
|
||||
|
||||
upstreamTemplate = $"{upstreamTemplate}{RegExMatchEndString}";
|
||||
|
||||
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider);
|
||||
|
||||
if (isAuthenticated)
|
||||
{
|
||||
var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
|
||||
reRoute.AuthenticationOptions.ProviderRootUrl, reRoute.AuthenticationOptions.ScopeName,
|
||||
reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes,
|
||||
reRoute.AuthenticationOptions.ScopeSecret);
|
||||
|
||||
var configHeaders = GetHeadersToAddToRequest(reRoute);
|
||||
|
||||
_reRoutes.Add(new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
|
||||
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
||||
authOptionsForRoute, configHeaders
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
_reRoutes.Add(new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod,
|
||||
upstreamTemplate, isAuthenticated, null, new List<ClaimToHeader>()));
|
||||
}
|
||||
}
|
||||
|
||||
private List<ClaimToHeader> GetHeadersToAddToRequest(YamlReRoute reRoute)
|
||||
{
|
||||
var configHeaders = new List<ClaimToHeader>();
|
||||
|
||||
foreach (var add in reRoute.AddHeadersToRequest)
|
||||
{
|
||||
var configurationHeader = _claimToHeaderConfigurationParser.Extract(add.Key, add.Value);
|
||||
|
||||
if (configurationHeader.IsError)
|
||||
{
|
||||
_logger.LogCritical(new EventId(1, "Application Failed to start"),
|
||||
$"Unable to extract configuration for key: {add.Key} and value: {add.Value} your configuration file is incorrect");
|
||||
|
||||
throw new Exception(configurationHeader.Errors[0].Message);
|
||||
}
|
||||
configHeaders.Add(configurationHeader.Data);
|
||||
}
|
||||
|
||||
return configHeaders;
|
||||
}
|
||||
|
||||
private bool IsPlaceHolder(string upstreamTemplate, int i)
|
||||
{
|
||||
return upstreamTemplate[i] == '{';
|
||||
}
|
||||
|
||||
public List<ReRoute> ReRoutes => _reRoutes;
|
||||
public List<ReRoute> ReRoutes { get; }
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ using Ocelot.Library.Errors;
|
||||
using Ocelot.Library.RequestBuilder;
|
||||
using Ocelot.Library.Responses;
|
||||
|
||||
namespace Ocelot.Library.Configuration
|
||||
namespace Ocelot.Library.Configuration.Parser
|
||||
{
|
||||
public class ClaimToHeaderConfigurationParser : IClaimToHeaderConfigurationParser
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using Ocelot.Library.Responses;
|
||||
|
||||
namespace Ocelot.Library.Configuration
|
||||
namespace Ocelot.Library.Configuration.Parser
|
||||
{
|
||||
public interface IClaimToHeaderConfigurationParser
|
||||
{
|
@ -0,0 +1,9 @@
|
||||
using Ocelot.Library.Responses;
|
||||
|
||||
namespace Ocelot.Library.Configuration.Provider
|
||||
{
|
||||
public interface IOcelotConfigurationProvider
|
||||
{
|
||||
Response<IOcelotConfiguration> Get();
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using Ocelot.Library.Responses;
|
||||
|
||||
namespace Ocelot.Library.Configuration.Repository
|
||||
{
|
||||
public interface IOcelotConfigurationRepository
|
||||
{
|
||||
Response<IOcelotConfiguration> Get();
|
||||
Response AddOrReplace(IOcelotConfiguration ocelotConfiguration);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
using Ocelot.Library.Responses;
|
||||
|
||||
namespace Ocelot.Library.Configuration.Repository
|
||||
{
|
||||
/// <summary>
|
||||
/// Register as singleton
|
||||
/// </summary>
|
||||
public class InMemoryOcelotConfigurationRepository : IOcelotConfigurationRepository
|
||||
{
|
||||
private static readonly object LockObject = new object();
|
||||
|
||||
private IOcelotConfiguration _ocelotConfiguration;
|
||||
|
||||
public Response<IOcelotConfiguration> Get()
|
||||
{
|
||||
return new OkResponse<IOcelotConfiguration>(_ocelotConfiguration);
|
||||
}
|
||||
|
||||
public Response AddOrReplace(IOcelotConfiguration ocelotConfiguration)
|
||||
{
|
||||
lock (LockObject)
|
||||
{
|
||||
_ocelotConfiguration = ocelotConfiguration;
|
||||
}
|
||||
|
||||
return new OkResponse();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ocelot.Library.Configuration.Creator;
|
||||
using Ocelot.Library.Configuration.Parser;
|
||||
using Ocelot.Library.Configuration.Repository;
|
||||
using Ocelot.Library.Errors;
|
||||
using Ocelot.Library.Responses;
|
||||
|
||||
namespace Ocelot.Library.Configuration.Yaml
|
||||
{
|
||||
/// <summary>
|
||||
/// Register as singleton
|
||||
/// </summary>
|
||||
public class YamlOcelotConfigurationCreator : IOcelotConfigurationCreator
|
||||
{
|
||||
private readonly IOptions<YamlConfiguration> _options;
|
||||
private readonly IConfigurationValidator _configurationValidator;
|
||||
private const string RegExMatchEverything = ".*";
|
||||
private const string RegExMatchEndString = "$";
|
||||
private readonly IClaimToHeaderConfigurationParser _claimToHeaderConfigurationParser;
|
||||
private readonly ILogger<YamlOcelotConfigurationCreator> _logger;
|
||||
|
||||
public YamlOcelotConfigurationCreator(
|
||||
IOptions<YamlConfiguration> options,
|
||||
IConfigurationValidator configurationValidator,
|
||||
IClaimToHeaderConfigurationParser claimToHeaderConfigurationParser,
|
||||
ILogger<YamlOcelotConfigurationCreator> logger)
|
||||
{
|
||||
_options = options;
|
||||
_configurationValidator = configurationValidator;
|
||||
_claimToHeaderConfigurationParser = claimToHeaderConfigurationParser;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Response<IOcelotConfiguration> Create()
|
||||
{
|
||||
var config = SetUpConfiguration();
|
||||
|
||||
return new OkResponse<IOcelotConfiguration>(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is meant to be tempoary to convert a yaml config to an ocelot config...probably wont keep this but we will see
|
||||
/// will need a refactor at some point as its crap
|
||||
/// </summary>
|
||||
private IOcelotConfiguration SetUpConfiguration()
|
||||
{
|
||||
var response = _configurationValidator.IsValid(_options.Value);
|
||||
|
||||
var reRoutes = new List<ReRoute>();
|
||||
|
||||
if (!response.IsError && !response.Data.IsError)
|
||||
{
|
||||
|
||||
foreach (var yamlReRoute in _options.Value.ReRoutes)
|
||||
{
|
||||
var ocelotReRoute = SetUpReRoute(yamlReRoute);
|
||||
reRoutes.Add(ocelotReRoute);
|
||||
}
|
||||
}
|
||||
|
||||
return new OcelotConfiguration(reRoutes);
|
||||
}
|
||||
|
||||
private ReRoute SetUpReRoute(YamlReRoute reRoute)
|
||||
{
|
||||
var upstreamTemplate = reRoute.UpstreamTemplate;
|
||||
|
||||
var placeholders = new List<string>();
|
||||
|
||||
for (var i = 0; i < upstreamTemplate.Length; i++)
|
||||
{
|
||||
if (IsPlaceHolder(upstreamTemplate, i))
|
||||
{
|
||||
var postitionOfPlaceHolderClosingBracket = upstreamTemplate.IndexOf('}', i);
|
||||
var difference = postitionOfPlaceHolderClosingBracket - i + 1;
|
||||
var variableName = upstreamTemplate.Substring(i, difference);
|
||||
placeholders.Add(variableName);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var placeholder in placeholders)
|
||||
{
|
||||
upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
|
||||
}
|
||||
|
||||
upstreamTemplate = $"{upstreamTemplate}{RegExMatchEndString}";
|
||||
|
||||
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider);
|
||||
|
||||
if (isAuthenticated)
|
||||
{
|
||||
var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
|
||||
reRoute.AuthenticationOptions.ProviderRootUrl, reRoute.AuthenticationOptions.ScopeName,
|
||||
reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes,
|
||||
reRoute.AuthenticationOptions.ScopeSecret);
|
||||
|
||||
var configHeaders = GetHeadersToAddToRequest(reRoute);
|
||||
|
||||
return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
|
||||
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
||||
authOptionsForRoute, configHeaders
|
||||
);
|
||||
}
|
||||
|
||||
return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod,
|
||||
upstreamTemplate, isAuthenticated, null, new List<ClaimToHeader>());
|
||||
}
|
||||
|
||||
private List<ClaimToHeader> GetHeadersToAddToRequest(YamlReRoute reRoute)
|
||||
{
|
||||
var configHeaders = new List<ClaimToHeader>();
|
||||
|
||||
foreach (var add in reRoute.AddHeadersToRequest)
|
||||
{
|
||||
var configurationHeader = _claimToHeaderConfigurationParser.Extract(add.Key, add.Value);
|
||||
|
||||
if (configurationHeader.IsError)
|
||||
{
|
||||
_logger.LogCritical(new EventId(1, "Application Failed to start"),
|
||||
$"Unable to extract configuration for key: {add.Key} and value: {add.Value} your configuration file is incorrect");
|
||||
|
||||
throw new Exception(configurationHeader.Errors[0].Message);
|
||||
}
|
||||
configHeaders.Add(configurationHeader.Data);
|
||||
}
|
||||
|
||||
return configHeaders;
|
||||
}
|
||||
|
||||
private bool IsPlaceHolder(string upstreamTemplate, int i)
|
||||
{
|
||||
return upstreamTemplate[i] == '{';
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
using Ocelot.Library.Configuration.Creator;
|
||||
using Ocelot.Library.Configuration.Provider;
|
||||
using Ocelot.Library.Configuration.Repository;
|
||||
using Ocelot.Library.Responses;
|
||||
|
||||
namespace Ocelot.Library.Configuration.Yaml
|
||||
{
|
||||
/// <summary>
|
||||
/// Register as singleton
|
||||
/// </summary>
|
||||
public class YamlOcelotConfigurationProvider : IOcelotConfigurationProvider
|
||||
{
|
||||
private readonly IOcelotConfigurationRepository _repo;
|
||||
private readonly IOcelotConfigurationCreator _creator;
|
||||
|
||||
public YamlOcelotConfigurationProvider(IOcelotConfigurationRepository repo,
|
||||
IOcelotConfigurationCreator creator)
|
||||
{
|
||||
_repo = repo;
|
||||
_creator = creator;
|
||||
}
|
||||
|
||||
public Response<IOcelotConfiguration> Get()
|
||||
{
|
||||
var config = _repo.Get();
|
||||
|
||||
if (config.IsError)
|
||||
{
|
||||
return new ErrorResponse<IOcelotConfiguration>(config.Errors);
|
||||
}
|
||||
|
||||
if (config.Data == null)
|
||||
{
|
||||
var configuration = _creator.Create();
|
||||
|
||||
if (configuration.IsError)
|
||||
{
|
||||
return new ErrorResponse<IOcelotConfiguration>(configuration.Errors);
|
||||
}
|
||||
|
||||
_repo.AddOrReplace(configuration.Data);
|
||||
|
||||
return new OkResponse<IOcelotConfiguration>(configuration.Data);
|
||||
}
|
||||
|
||||
return new OkResponse<IOcelotConfiguration>(config.Data);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,9 @@
|
||||
namespace Ocelot.Library.DependencyInjection
|
||||
using Ocelot.Library.Configuration.Creator;
|
||||
using Ocelot.Library.Configuration.Parser;
|
||||
using Ocelot.Library.Configuration.Provider;
|
||||
using Ocelot.Library.Configuration.Repository;
|
||||
|
||||
namespace Ocelot.Library.DependencyInjection
|
||||
{
|
||||
using Authentication;
|
||||
using Configuration;
|
||||
@ -16,22 +21,30 @@
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddOcelot(this IServiceCollection services, IConfigurationRoot configurationRoot)
|
||||
public static IServiceCollection AddOcelotYamlConfiguration(this IServiceCollection services, IConfigurationRoot configurationRoot)
|
||||
{
|
||||
// framework services
|
||||
services.AddOptions();
|
||||
services.AddMvcCore().AddJsonFormatters();
|
||||
services.AddLogging();
|
||||
|
||||
// initial configuration from yaml
|
||||
services.Configure<YamlConfiguration>(configurationRoot);
|
||||
|
||||
// ocelot services.
|
||||
services.AddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
|
||||
services.AddSingleton<IClaimsParser, ClaimsParser>();
|
||||
services.AddSingleton<IOcelotConfigurationCreator, YamlOcelotConfigurationCreator>();
|
||||
services.AddSingleton<IOcelotConfigurationProvider, YamlOcelotConfigurationProvider>();
|
||||
services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
||||
services.AddSingleton<IClaimToHeaderConfigurationParser, ClaimToHeaderConfigurationParser>();
|
||||
services.AddSingleton<IConfigurationValidator, ConfigurationValidator>();
|
||||
services.AddSingleton<IOcelotConfiguration, OcelotConfiguration>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOcelot(this IServiceCollection services)
|
||||
{
|
||||
// framework services
|
||||
services.AddMvcCore().AddJsonFormatters();
|
||||
services.AddLogging();
|
||||
|
||||
// ocelot services.
|
||||
services.AddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
|
||||
services.AddSingleton<IClaimsParser, ClaimsParser>();
|
||||
services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
|
||||
services.AddSingleton<ITemplateVariableNameAndValueFinder, TemplateVariableNameAndValueFinder>();
|
||||
services.AddSingleton<IDownstreamUrlTemplateVariableReplacer, DownstreamUrlTemplateVariableReplacer>();
|
||||
|
@ -1,29 +1,31 @@
|
||||
namespace Ocelot.Library.DownstreamRouteFinder
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Configuration;
|
||||
using Errors;
|
||||
using Responses;
|
||||
using UrlMatcher;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ocelot.Library.Configuration.Provider;
|
||||
using Ocelot.Library.Errors;
|
||||
using Ocelot.Library.Responses;
|
||||
using Ocelot.Library.UrlMatcher;
|
||||
|
||||
namespace Ocelot.Library.DownstreamRouteFinder
|
||||
{
|
||||
public class DownstreamRouteFinder : IDownstreamRouteFinder
|
||||
{
|
||||
private readonly IOcelotConfiguration _configuration;
|
||||
private readonly IOcelotConfigurationProvider _configProvider;
|
||||
private readonly IUrlPathToUrlTemplateMatcher _urlMatcher;
|
||||
private readonly ITemplateVariableNameAndValueFinder _templateVariableNameAndValueFinder;
|
||||
|
||||
public DownstreamRouteFinder(IOcelotConfiguration configuration, IUrlPathToUrlTemplateMatcher urlMatcher, ITemplateVariableNameAndValueFinder templateVariableNameAndValueFinder)
|
||||
public DownstreamRouteFinder(IOcelotConfigurationProvider configProvider, IUrlPathToUrlTemplateMatcher urlMatcher, ITemplateVariableNameAndValueFinder templateVariableNameAndValueFinder)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_configProvider = configProvider;
|
||||
_urlMatcher = urlMatcher;
|
||||
_templateVariableNameAndValueFinder = templateVariableNameAndValueFinder;
|
||||
}
|
||||
|
||||
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
|
||||
{
|
||||
foreach (var template in _configuration.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase)))
|
||||
var configuration = _configProvider.Get();
|
||||
|
||||
foreach (var template in configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase)))
|
||||
{
|
||||
var urlMatch = _urlMatcher.Match(upstreamUrlPath, template.UpstreamTemplatePattern);
|
||||
|
||||
|
@ -28,7 +28,8 @@ namespace Ocelot
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddOcelot(Configuration);
|
||||
services.AddOcelotYamlConfiguration(Configuration);
|
||||
services.AddOcelot();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
Reference in New Issue
Block a user