mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-20 17:42:50 +08:00
Added some basic cache stuff
This commit is contained in:
parent
5afcebe7cb
commit
47afc850ff
13
README.md
13
README.md
@ -81,12 +81,21 @@ This is pretty much all you need to get going.......more to come!
|
|||||||
|
|
||||||
Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> as such you can use any logging provider you like such as default, nlog, serilog or whatever you want.
|
Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> as such you can use any logging provider you like such as default, nlog, serilog or whatever you want.
|
||||||
|
|
||||||
|
## Caching
|
||||||
|
|
||||||
|
Ocelot supports some very rudimentary caching at the moment provider by the [CacheManager](http://cachemanager.net/) project. This is an amazing project
|
||||||
|
that is solving a lot of caching problems. I would reccomend using this package to cache with Ocelot. If you look at the example [here](https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/Startup.cs)
|
||||||
|
you can see how the cache manager is setup and then passed into the Ocelot AddOcelotOutputCaching configuration method. You can
|
||||||
|
use any settings supported by the CacheManager package and just pass them in.
|
||||||
|
|
||||||
|
Anyway Ocelot currently supports caching on the URL of the downstream service and setting a TTL in seconds to expire the cache. More to come!
|
||||||
|
|
||||||
## Not supported
|
## Not supported
|
||||||
|
|
||||||
Ocelot does not support...
|
Ocelot does not support...
|
||||||
- Chunked Encoding - Ocelot will always get the body size and return Content-Length header. Sorry
|
* Chunked Encoding - Ocelot will always get the body size and return Content-Length header. Sorry
|
||||||
if this doesn't work for your use case!
|
if this doesn't work for your use case!
|
||||||
- Fowarding a host header - The host header that you send to Ocelot will not be forwarded to
|
* Fowarding a host header - The host header that you send to Ocelot will not be forwarded to
|
||||||
the downstream service. Obviously this would break everything :(
|
the downstream service. Obviously this would break everything :(
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,5 +60,11 @@
|
|||||||
# This tells Ocelot to look for a header and use its value as a request/correlation id.
|
# This tells Ocelot to look for a header and use its value as a request/correlation id.
|
||||||
# If it is set here then the id will be forwarded to the downstream service. If it
|
# If it is set here then the id will be forwarded to the downstream service. If it
|
||||||
# does not then it will not be forwarded
|
# does not then it will not be forwarded
|
||||||
"RequestIdKey": "OcRequestId"
|
"RequestIdKey": "OcRequestId",
|
||||||
|
# If this is set the response from the downstream service will be cached using the key that called it.
|
||||||
|
# This gives the user a chance to influence the key by adding some random query string paramter for
|
||||||
|
# a user id or something that would get ignored by the downstream service. This is a hack and I
|
||||||
|
# intend to provide a mechanism the user can specify for the ttl caching. Also want to expand
|
||||||
|
# the caching a lot.
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
}
|
}
|
10
src/Ocelot/Cache/IOcelotCache.cs
Normal file
10
src/Ocelot/Cache/IOcelotCache.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ocelot.Cache
|
||||||
|
{
|
||||||
|
public interface IOcelotCache<T>
|
||||||
|
{
|
||||||
|
void Add(string key, T value, TimeSpan ttl);
|
||||||
|
T Get(string key);
|
||||||
|
}
|
||||||
|
}
|
69
src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
Normal file
69
src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
|
||||||
|
namespace Ocelot.Cache.Middleware
|
||||||
|
{
|
||||||
|
public class OutputCacheMiddleware : OcelotMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly IOcelotCache<HttpResponseMessage> _outputCache;
|
||||||
|
|
||||||
|
public OutputCacheMiddleware(RequestDelegate next,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
|
IRequestScopedDataRepository scopedDataRepository,
|
||||||
|
IOcelotCache<HttpResponseMessage> outputCache)
|
||||||
|
:base(scopedDataRepository)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_outputCache = outputCache;
|
||||||
|
_logger = loggerFactory.CreateLogger<OutputCacheMiddleware>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
var downstreamUrlKey = DownstreamUrl;
|
||||||
|
|
||||||
|
if (!DownstreamRoute.ReRoute.IsCached)
|
||||||
|
{
|
||||||
|
await _next.Invoke(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("OutputCacheMiddleware.Invoke stared checking cache for downstreamUrlKey", downstreamUrlKey);
|
||||||
|
|
||||||
|
var cached = _outputCache.Get(downstreamUrlKey);
|
||||||
|
|
||||||
|
if (cached != null)
|
||||||
|
{
|
||||||
|
SetHttpResponseMessageThisRequest(cached);
|
||||||
|
|
||||||
|
_logger.LogDebug("OutputCacheMiddleware.Invoke finished returned cached response for downstreamUrlKey", downstreamUrlKey);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("OutputCacheMiddleware.Invoke no resonse cached for downstreamUrlKey", downstreamUrlKey);
|
||||||
|
|
||||||
|
await _next.Invoke(context);
|
||||||
|
|
||||||
|
if (PipelineError())
|
||||||
|
{
|
||||||
|
_logger.LogDebug("OutputCacheMiddleware.Invoke there was a pipeline error for downstreamUrlKey", downstreamUrlKey);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = HttpResponseMessage;
|
||||||
|
|
||||||
|
_outputCache.Add(downstreamUrlKey, response, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.FileCacheOptions.TtlSeconds));
|
||||||
|
|
||||||
|
_logger.LogDebug("OutputCacheMiddleware.Invoke finished response added to cache for downstreamUrlKey", downstreamUrlKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Ocelot.Cache.Middleware
|
||||||
|
{
|
||||||
|
public static class OutputCacheMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseOutputCacheMiddleware(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<OutputCacheMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/Ocelot/Cache/OcelotCacheManagerCache.cs
Normal file
25
src/Ocelot/Cache/OcelotCacheManagerCache.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using CacheManager.Core;
|
||||||
|
|
||||||
|
namespace Ocelot.Cache
|
||||||
|
{
|
||||||
|
public class OcelotCacheManagerCache<T> : IOcelotCache<T>
|
||||||
|
{
|
||||||
|
private readonly ICacheManager<T> _cacheManager;
|
||||||
|
|
||||||
|
public OcelotCacheManagerCache(ICacheManager<T> cacheManager)
|
||||||
|
{
|
||||||
|
_cacheManager = cacheManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(string key, T value, TimeSpan ttl)
|
||||||
|
{
|
||||||
|
_cacheManager.Add(new CacheItem<T>(key, value, ExpirationMode.Absolute, ttl));
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Get(string key)
|
||||||
|
{
|
||||||
|
return _cacheManager.Get<T>(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,8 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private bool _isAuthorised;
|
private bool _isAuthorised;
|
||||||
private List<ClaimToThing> _claimToQueries;
|
private List<ClaimToThing> _claimToQueries;
|
||||||
private string _requestIdHeaderKey;
|
private string _requestIdHeaderKey;
|
||||||
|
private bool _isCached;
|
||||||
|
private CacheOptions _fileCacheOptions;
|
||||||
|
|
||||||
public ReRouteBuilder()
|
public ReRouteBuilder()
|
||||||
{
|
{
|
||||||
@ -127,9 +129,24 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReRouteBuilder WithIsCached(bool input)
|
||||||
|
{
|
||||||
|
_isCached = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReRouteBuilder WithCacheOptions(CacheOptions input)
|
||||||
|
{
|
||||||
|
_fileCacheOptions = input;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ReRoute Build()
|
public ReRoute Build()
|
||||||
{
|
{
|
||||||
return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, _isAuthorised, _claimToQueries, _requestIdHeaderKey);
|
return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern,
|
||||||
|
_isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName,
|
||||||
|
_requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement,
|
||||||
|
_isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/Ocelot/Configuration/CacheOptions.cs
Normal file
12
src/Ocelot/Configuration/CacheOptions.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Ocelot.Configuration
|
||||||
|
{
|
||||||
|
public class CacheOptions
|
||||||
|
{
|
||||||
|
public CacheOptions(int ttlSeconds)
|
||||||
|
{
|
||||||
|
TtlSeconds = ttlSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TtlSeconds { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
@ -100,6 +100,8 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
var isAuthorised = reRoute.RouteClaimsRequirement?.Count > 0;
|
var isAuthorised = reRoute.RouteClaimsRequirement?.Count > 0;
|
||||||
|
|
||||||
|
var isCached = reRoute.FileCacheOptions.TtlSeconds > 0;
|
||||||
|
|
||||||
if (isAuthenticated)
|
if (isAuthenticated)
|
||||||
{
|
{
|
||||||
var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
|
var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
|
||||||
@ -115,7 +117,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
||||||
authOptionsForRoute, claimsToHeaders, claimsToClaims,
|
authOptionsForRoute, claimsToHeaders, claimsToClaims,
|
||||||
reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries,
|
reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries,
|
||||||
reRoute.RequestIdKey
|
reRoute.RequestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +125,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
|
||||||
null, new List<ClaimToThing>(), new List<ClaimToThing>(),
|
null, new List<ClaimToThing>(), new List<ClaimToThing>(),
|
||||||
reRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>(),
|
reRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>(),
|
||||||
reRoute.RequestIdKey);
|
reRoute.RequestIdKey, isCached, new CacheOptions(reRoute.FileCacheOptions.TtlSeconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
|
private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
|
||||||
|
7
src/Ocelot/Configuration/File/FileCacheOptions.cs
Normal file
7
src/Ocelot/Configuration/File/FileCacheOptions.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ocelot.Configuration.File
|
||||||
|
{
|
||||||
|
public class FileCacheOptions
|
||||||
|
{
|
||||||
|
public int TtlSeconds { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ namespace Ocelot.Configuration.File
|
|||||||
RouteClaimsRequirement = new Dictionary<string, string>();
|
RouteClaimsRequirement = new Dictionary<string, string>();
|
||||||
AddQueriesToRequest = new Dictionary<string, string>();
|
AddQueriesToRequest = new Dictionary<string, string>();
|
||||||
AuthenticationOptions = new FileAuthenticationOptions();
|
AuthenticationOptions = new FileAuthenticationOptions();
|
||||||
|
FileCacheOptions = new FileCacheOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DownstreamTemplate { get; set; }
|
public string DownstreamTemplate { get; set; }
|
||||||
@ -22,5 +23,6 @@ namespace Ocelot.Configuration.File
|
|||||||
public Dictionary<string, string> RouteClaimsRequirement { get; set; }
|
public Dictionary<string, string> RouteClaimsRequirement { get; set; }
|
||||||
public Dictionary<string, string> AddQueriesToRequest { get; set; }
|
public Dictionary<string, string> AddQueriesToRequest { get; set; }
|
||||||
public string RequestIdKey { get; set; }
|
public string RequestIdKey { get; set; }
|
||||||
|
public FileCacheOptions FileCacheOptions { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,7 +4,10 @@ namespace Ocelot.Configuration
|
|||||||
{
|
{
|
||||||
public class ReRoute
|
public class ReRoute
|
||||||
{
|
{
|
||||||
public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions, List<ClaimToThing> configurationHeaderExtractorProperties, List<ClaimToThing> claimsToClaims, Dictionary<string, string> routeClaimsRequirement, bool isAuthorised, List<ClaimToThing> claimsToQueries, string requestIdKey)
|
public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern,
|
||||||
|
bool isAuthenticated, AuthenticationOptions authenticationOptions, List<ClaimToThing> configurationHeaderExtractorProperties,
|
||||||
|
List<ClaimToThing> claimsToClaims, Dictionary<string, string> routeClaimsRequirement, bool isAuthorised, List<ClaimToThing> claimsToQueries,
|
||||||
|
string requestIdKey, bool isCached, CacheOptions fileCacheOptions)
|
||||||
{
|
{
|
||||||
DownstreamTemplate = downstreamTemplate;
|
DownstreamTemplate = downstreamTemplate;
|
||||||
UpstreamTemplate = upstreamTemplate;
|
UpstreamTemplate = upstreamTemplate;
|
||||||
@ -15,6 +18,8 @@ namespace Ocelot.Configuration
|
|||||||
RouteClaimsRequirement = routeClaimsRequirement;
|
RouteClaimsRequirement = routeClaimsRequirement;
|
||||||
IsAuthorised = isAuthorised;
|
IsAuthorised = isAuthorised;
|
||||||
RequestIdKey = requestIdKey;
|
RequestIdKey = requestIdKey;
|
||||||
|
IsCached = isCached;
|
||||||
|
FileCacheOptions = fileCacheOptions;
|
||||||
ClaimsToQueries = claimsToQueries
|
ClaimsToQueries = claimsToQueries
|
||||||
?? new List<ClaimToThing>();
|
?? new List<ClaimToThing>();
|
||||||
ClaimsToClaims = claimsToClaims
|
ClaimsToClaims = claimsToClaims
|
||||||
@ -35,5 +40,7 @@ namespace Ocelot.Configuration
|
|||||||
public List<ClaimToThing> ClaimsToClaims { get; private set; }
|
public List<ClaimToThing> ClaimsToClaims { get; private set; }
|
||||||
public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
|
public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
|
||||||
public string RequestIdKey { get; private set; }
|
public string RequestIdKey { get; private set; }
|
||||||
|
public bool IsCached { get; private set; }
|
||||||
|
public CacheOptions FileCacheOptions { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,14 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using CacheManager.Core;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Ocelot.Authentication.Handler.Creator;
|
using Ocelot.Authentication.Handler.Creator;
|
||||||
using Ocelot.Authentication.Handler.Factory;
|
using Ocelot.Authentication.Handler.Factory;
|
||||||
using Ocelot.Authorisation;
|
using Ocelot.Authorisation;
|
||||||
|
using Ocelot.Cache;
|
||||||
using Ocelot.Claims;
|
using Ocelot.Claims;
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
@ -26,11 +31,21 @@ namespace Ocelot.DependencyInjection
|
|||||||
{
|
{
|
||||||
public static class ServiceCollectionExtensions
|
public static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public static IServiceCollection AddOcelotOutputCaching(this IServiceCollection services, Action<ConfigurationBuilderCachePart> settings)
|
||||||
|
{
|
||||||
|
var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings);
|
||||||
|
var ocelotCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache);
|
||||||
|
|
||||||
|
services.AddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache);
|
||||||
|
services.AddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotCacheManager);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
public static IServiceCollection AddOcelotFileConfiguration(this IServiceCollection services, IConfigurationRoot configurationRoot)
|
public static IServiceCollection AddOcelotFileConfiguration(this IServiceCollection services, IConfigurationRoot configurationRoot)
|
||||||
{
|
{
|
||||||
services.Configure<FileConfiguration>(configurationRoot);
|
services.Configure<FileConfiguration>(configurationRoot);
|
||||||
|
|
||||||
// ocelot services.
|
|
||||||
services.AddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
services.AddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
||||||
services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
||||||
services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
|
services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
|
||||||
@ -40,11 +55,9 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
public static IServiceCollection AddOcelot(this IServiceCollection services)
|
public static IServiceCollection AddOcelot(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
// framework services dependency for the identity server middleware
|
|
||||||
services.AddMvcCore().AddJsonFormatters();
|
services.AddMvcCore().AddJsonFormatters();
|
||||||
services.AddLogging();
|
services.AddLogging();
|
||||||
|
|
||||||
// ocelot services.
|
|
||||||
services.AddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
|
services.AddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
|
||||||
services.AddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
|
services.AddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
|
||||||
services.AddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
|
services.AddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
using Ocelot.DownstreamRouteFinder;
|
using Ocelot.DownstreamRouteFinder;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
@ -59,6 +60,15 @@ namespace Ocelot.Middleware
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpResponseMessage HttpResponseMessage
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var request = _requestScopedDataRepository.Get<HttpResponseMessage>("HttpResponseMessage");
|
||||||
|
return request.Data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SetDownstreamRouteForThisRequest(DownstreamRoute downstreamRoute)
|
public void SetDownstreamRouteForThisRequest(DownstreamRoute downstreamRoute)
|
||||||
{
|
{
|
||||||
_requestScopedDataRepository.Add("DownstreamRoute", downstreamRoute);
|
_requestScopedDataRepository.Add("DownstreamRoute", downstreamRoute);
|
||||||
@ -73,5 +83,11 @@ namespace Ocelot.Middleware
|
|||||||
{
|
{
|
||||||
_requestScopedDataRepository.Add("Request", request);
|
_requestScopedDataRepository.Add("Request", request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetHttpResponseMessageThisRequest(HttpResponseMessage responseMessage)
|
||||||
|
{
|
||||||
|
_requestScopedDataRepository.Add("HttpResponseMessage", responseMessage);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Ocelot.Authentication.Middleware;
|
using Ocelot.Authentication.Middleware;
|
||||||
|
using Ocelot.Cache.Middleware;
|
||||||
using Ocelot.Claims.Middleware;
|
using Ocelot.Claims.Middleware;
|
||||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||||
using Ocelot.DownstreamUrlCreator.Middleware;
|
using Ocelot.DownstreamUrlCreator.Middleware;
|
||||||
@ -99,6 +100,10 @@ namespace Ocelot.Middleware
|
|||||||
// This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
|
// This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
|
||||||
builder.UseDownstreamUrlCreatorMiddleware();
|
builder.UseDownstreamUrlCreatorMiddleware();
|
||||||
|
|
||||||
|
// Not sure if this is the best place for this but we use the downstream url
|
||||||
|
// as the basis for our cache key.
|
||||||
|
builder.UseOutputCacheMiddleware();
|
||||||
|
|
||||||
// Everything should now be ready to build or HttpRequest
|
// Everything should now be ready to build or HttpRequest
|
||||||
builder.UseHttpRequestBuilderMiddleware();
|
builder.UseHttpRequestBuilderMiddleware();
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Responder;
|
|
||||||
|
|
||||||
namespace Ocelot.Requester.Middleware
|
namespace Ocelot.Requester.Middleware
|
||||||
{
|
{
|
||||||
@ -12,17 +9,14 @@ namespace Ocelot.Requester.Middleware
|
|||||||
{
|
{
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
private readonly IHttpRequester _requester;
|
private readonly IHttpRequester _requester;
|
||||||
private readonly IHttpResponder _responder;
|
|
||||||
|
|
||||||
public HttpRequesterMiddleware(RequestDelegate next,
|
public HttpRequesterMiddleware(RequestDelegate next,
|
||||||
IHttpRequester requester,
|
IHttpRequester requester,
|
||||||
IRequestScopedDataRepository requestScopedDataRepository,
|
IRequestScopedDataRepository requestScopedDataRepository)
|
||||||
IHttpResponder responder)
|
|
||||||
:base(requestScopedDataRepository)
|
:base(requestScopedDataRepository)
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_requester = requester;
|
_requester = requester;
|
||||||
_responder = responder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(HttpContext context)
|
||||||
@ -35,12 +29,7 @@ namespace Ocelot.Requester.Middleware
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var setResponse = await _responder.SetResponseOnHttpContext(context, response.Data);
|
SetHttpResponseMessageThisRequest(response.Data);
|
||||||
|
|
||||||
if (setResponse.IsError)
|
|
||||||
{
|
|
||||||
SetPipelineError(response.Errors);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -38,7 +38,7 @@ namespace Ocelot.Responder
|
|||||||
AddHeaderIfDoesntExist(context, httpResponseHeader);
|
AddHeaderIfDoesntExist(context, httpResponseHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
var content = await response.Content.ReadAsStreamAsync();
|
var content = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
|
||||||
AddHeaderIfDoesntExist(context, new KeyValuePair<string, IEnumerable<string>>("Content-Length", new []{ content.Length.ToString() }) );
|
AddHeaderIfDoesntExist(context, new KeyValuePair<string, IEnumerable<string>>("Content-Length", new []{ content.Length.ToString() }) );
|
||||||
|
|
||||||
@ -52,12 +52,10 @@ namespace Ocelot.Responder
|
|||||||
|
|
||||||
}, context);
|
}, context);
|
||||||
|
|
||||||
using (var reader = new StreamReader(content))
|
using (Stream stream = new MemoryStream(content))
|
||||||
{
|
{
|
||||||
var responseContent = reader.ReadToEnd();
|
await stream.CopyToAsync(context.Response.Body);
|
||||||
await context.Response.WriteAsync(responseContent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OkResponse();
|
return new OkResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Net.Http;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Errors;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
|
||||||
@ -30,18 +31,32 @@ namespace Ocelot.Responder.Middleware
|
|||||||
if (PipelineError())
|
if (PipelineError())
|
||||||
{
|
{
|
||||||
var errors = GetPipelineErrors();
|
var errors = GetPipelineErrors();
|
||||||
|
|
||||||
var statusCode = _codeMapper.Map(errors);
|
|
||||||
|
|
||||||
if (!statusCode.IsError)
|
await SetErrorResponse(context, errors);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var setResponse = await _responder.SetResponseOnHttpContext(context, HttpResponseMessage);
|
||||||
|
|
||||||
|
if (setResponse.IsError)
|
||||||
{
|
{
|
||||||
await _responder.SetErrorResponseOnContext(context, statusCode.Data);
|
await SetErrorResponse(context, setResponse.Errors);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _responder.SetErrorResponseOnContext(context, 500);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SetErrorResponse(HttpContext context, List<Error> errors)
|
||||||
|
{
|
||||||
|
var statusCode = _codeMapper.Map(errors);
|
||||||
|
|
||||||
|
if (!statusCode.IsError)
|
||||||
|
{
|
||||||
|
await _responder.SetErrorResponseOnContext(context, statusCode.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _responder.SetErrorResponseOnContext(context, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,7 +27,10 @@
|
|||||||
"Microsoft.NETCore.App": {
|
"Microsoft.NETCore.App": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"type": "platform"
|
"type": "platform"
|
||||||
}
|
},
|
||||||
|
"CacheManager.Core": "0.9.1",
|
||||||
|
"CacheManager.Microsoft.Extensions.Configuration": "0.9.1",
|
||||||
|
"CacheManager.Microsoft.Extensions.Logging": "0.9.1"
|
||||||
},
|
},
|
||||||
|
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
130
test/Ocelot.AcceptanceTests/CachingTests.cs
Normal file
130
test/Ocelot.AcceptanceTests/CachingTests.cs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.AcceptanceTests
|
||||||
|
{
|
||||||
|
public class CachingTests : IDisposable
|
||||||
|
{
|
||||||
|
private IWebHost _builder;
|
||||||
|
private readonly Steps _steps;
|
||||||
|
|
||||||
|
public CachingTests()
|
||||||
|
{
|
||||||
|
_steps = new Steps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_cached_response()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51879/",
|
||||||
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
FileCacheOptions = new FileCacheOptions
|
||||||
|
{
|
||||||
|
TtlSeconds = 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51879", 200, "Hello from Tom"))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_not_return_cached_response_as_ttl_expires()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51879/",
|
||||||
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
FileCacheOptions = new FileCacheOptions
|
||||||
|
{
|
||||||
|
TtlSeconds = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51879", 200, "Hello from Tom"))
|
||||||
|
.And(x => x.GivenTheCacheExpires())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheCacheExpires()
|
||||||
|
{
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheServiceNowReturns(string url, int statusCode, string responseBody)
|
||||||
|
{
|
||||||
|
_builder.Dispose();
|
||||||
|
GivenThereIsAServiceRunningOn(url, statusCode, responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
||||||
|
{
|
||||||
|
_builder = new WebHostBuilder()
|
||||||
|
.UseUrls(url)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.UseUrls(url)
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
await context.Response.WriteAsync(responseBody);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_builder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_builder?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,12 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -21,21 +18,160 @@ namespace Ocelot.AcceptanceTests
|
|||||||
private readonly string _configurationPath;
|
private readonly string _configurationPath;
|
||||||
private IWebHost _builder;
|
private IWebHost _builder;
|
||||||
private readonly Steps _steps;
|
private readonly Steps _steps;
|
||||||
|
private int _counter;
|
||||||
|
|
||||||
public CustomMiddlewareTests()
|
public CustomMiddlewareTests()
|
||||||
{
|
{
|
||||||
|
_counter = 0;
|
||||||
_steps = new Steps();;
|
_steps = new Steps();;
|
||||||
_configurationPath = "configuration.json";
|
_configurationPath = "configuration.json";
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void response_should_come_from_pre_authorisation_middleware()
|
public void should_call_pre_query_string_builder_middleware()
|
||||||
|
{
|
||||||
|
var configuration = new OcelotMiddlewareConfiguration
|
||||||
|
{
|
||||||
|
AuthorisationMiddleware = async (ctx, next) =>
|
||||||
|
{
|
||||||
|
_counter++;
|
||||||
|
await next.Invoke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileConfiguration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:41879/",
|
||||||
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning(configuration))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => x.ThenTheCounterIs(1))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_call_authorisation_middleware()
|
||||||
|
{
|
||||||
|
var configuration = new OcelotMiddlewareConfiguration
|
||||||
|
{
|
||||||
|
AuthorisationMiddleware = async (ctx, next) =>
|
||||||
|
{
|
||||||
|
_counter++;
|
||||||
|
await next.Invoke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileConfiguration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:41879/",
|
||||||
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning(configuration))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => x.ThenTheCounterIs(1))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_call_authentication_middleware()
|
||||||
|
{
|
||||||
|
var configuration = new OcelotMiddlewareConfiguration
|
||||||
|
{
|
||||||
|
AuthenticationMiddleware = async (ctx, next) =>
|
||||||
|
{
|
||||||
|
_counter++;
|
||||||
|
await next.Invoke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileConfiguration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:41879/",
|
||||||
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning(configuration))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => x.ThenTheCounterIs(1))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_call_pre_error_middleware()
|
||||||
|
{
|
||||||
|
var configuration = new OcelotMiddlewareConfiguration
|
||||||
|
{
|
||||||
|
PreErrorResponderMiddleware = async (ctx, next) =>
|
||||||
|
{
|
||||||
|
_counter++;
|
||||||
|
await next.Invoke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileConfiguration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:41879/",
|
||||||
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Get",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:41879", 200))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning(configuration))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => x.ThenTheCounterIs(1))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_call_pre_authorisation_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotMiddlewareConfiguration
|
var configuration = new OcelotMiddlewareConfiguration
|
||||||
{
|
{
|
||||||
PreAuthorisationMiddleware = async (ctx, next) =>
|
PreAuthorisationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
await ctx.Response.WriteAsync("PreHttpResponderMiddleware");
|
_counter++;
|
||||||
|
await next.Invoke();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,18 +193,19 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.And(x => _steps.GivenOcelotIsRunning(configuration))
|
.And(x => _steps.GivenOcelotIsRunning(configuration))
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("PreHttpResponderMiddleware"))
|
.And(x => x.ThenTheCounterIs(1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void response_should_come_from_pre_http_authentication_middleware()
|
public void should_call_pre_http_authentication_middleware()
|
||||||
{
|
{
|
||||||
var configuration = new OcelotMiddlewareConfiguration
|
var configuration = new OcelotMiddlewareConfiguration
|
||||||
{
|
{
|
||||||
PreAuthenticationMiddleware = async (ctx, next) =>
|
PreAuthenticationMiddleware = async (ctx, next) =>
|
||||||
{
|
{
|
||||||
await ctx.Response.WriteAsync("PreHttpRequesterMiddleware");
|
_counter++;
|
||||||
|
await next.Invoke();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,10 +227,15 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.And(x => _steps.GivenOcelotIsRunning(configuration))
|
.And(x => _steps.GivenOcelotIsRunning(configuration))
|
||||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
.And(x => _steps.ThenTheResponseBodyShouldBe("PreHttpRequesterMiddleware"))
|
.And(x => x.ThenTheCounterIs(1))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenTheCounterIs(int expected)
|
||||||
|
{
|
||||||
|
_counter.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
|
||||||
{
|
{
|
||||||
_builder = new WebHostBuilder()
|
_builder = new WebHostBuilder()
|
||||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using CacheManager.Core;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.TestHost;
|
using Microsoft.AspNetCore.TestHost;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
@ -15,6 +16,7 @@ using Ocelot.DependencyInjection;
|
|||||||
using Ocelot.ManualTest;
|
using Ocelot.ManualTest;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
|
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
|
||||||
|
|
||||||
namespace Ocelot.AcceptanceTests
|
namespace Ocelot.AcceptanceTests
|
||||||
{
|
{
|
||||||
@ -82,6 +84,16 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.UseConfiguration(configuration)
|
.UseConfiguration(configuration)
|
||||||
.ConfigureServices(s =>
|
.ConfigureServices(s =>
|
||||||
{
|
{
|
||||||
|
Action<ConfigurationBuilderCachePart> settings = (x) =>
|
||||||
|
{
|
||||||
|
x.WithMicrosoftLogging(log =>
|
||||||
|
{
|
||||||
|
log.AddConsole(LogLevel.Debug);
|
||||||
|
})
|
||||||
|
.WithDictionaryHandle();
|
||||||
|
};
|
||||||
|
|
||||||
|
s.AddOcelotOutputCaching(settings);
|
||||||
s.AddOcelotFileConfiguration(configuration);
|
s.AddOcelotFileConfiguration(configuration);
|
||||||
s.AddOcelot();
|
s.AddOcelot();
|
||||||
})
|
})
|
||||||
|
@ -1 +1 @@
|
|||||||
{"ReRoutes":[{"DownstreamTemplate":"http://localhost:41879/","UpstreamTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null}]}
|
{"ReRoutes":[{"DownstreamTemplate":"http://localhost:41879/","UpstreamTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0}}]}
|
@ -1,10 +1,13 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using System;
|
||||||
|
using CacheManager.Core;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Ocelot.DependencyInjection;
|
using Ocelot.DependencyInjection;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
|
||||||
|
|
||||||
namespace Ocelot.ManualTest
|
namespace Ocelot.ManualTest
|
||||||
{
|
{
|
||||||
@ -27,6 +30,16 @@ namespace Ocelot.ManualTest
|
|||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
Action<ConfigurationBuilderCachePart> settings = (x) =>
|
||||||
|
{
|
||||||
|
x.WithMicrosoftLogging(log =>
|
||||||
|
{
|
||||||
|
log.AddConsole(LogLevel.Debug);
|
||||||
|
})
|
||||||
|
.WithDictionaryHandle();
|
||||||
|
};
|
||||||
|
|
||||||
|
services.AddOcelotOutputCaching(settings);
|
||||||
services.AddOcelotFileConfiguration(Configuration);
|
services.AddOcelotFileConfiguration(Configuration);
|
||||||
services.AddOcelot();
|
services.AddOcelot();
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,8 @@
|
|||||||
{
|
{
|
||||||
"DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts",
|
"DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts",
|
||||||
"UpstreamTemplate": "/posts",
|
"UpstreamTemplate": "/posts",
|
||||||
"UpstreamHttpMethod": "Get"
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}",
|
"DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts/{postId}",
|
||||||
|
77
test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs
Normal file
77
test/Ocelot.UnitTests/Cache/CacheManagerCacheTests.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
using CacheManager.Core;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Cache;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Cache
|
||||||
|
{
|
||||||
|
public class CacheManagerCacheTests
|
||||||
|
{
|
||||||
|
private OcelotCacheManagerCache<string> _ocelotOcelotCacheManager;
|
||||||
|
private Mock<ICacheManager<string>> _mockCacheManager;
|
||||||
|
private string _key;
|
||||||
|
private string _value;
|
||||||
|
private string _resultGet;
|
||||||
|
private TimeSpan _ttlSeconds;
|
||||||
|
|
||||||
|
public CacheManagerCacheTests()
|
||||||
|
{
|
||||||
|
_mockCacheManager = new Mock<ICacheManager<string>>();
|
||||||
|
_ocelotOcelotCacheManager = new OcelotCacheManagerCache<string>(_mockCacheManager.Object);
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public void should_get_from_cache()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheFollowingIsCached("someKey", "someValue"))
|
||||||
|
.When(x => x.WhenIGetFromTheCache())
|
||||||
|
.Then(x => x.ThenTheResultIs("someValue"))
|
||||||
|
.BDDfy();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_add_to_cache()
|
||||||
|
{
|
||||||
|
this.When(x => x.WhenIAddToTheCache("someKey", "someValue", TimeSpan.FromSeconds(1)))
|
||||||
|
.Then(x => x.ThenTheCacheIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIAddToTheCache(string key, string value, TimeSpan ttlSeconds)
|
||||||
|
{
|
||||||
|
_key = key;
|
||||||
|
_value = value;
|
||||||
|
_ttlSeconds = ttlSeconds;
|
||||||
|
|
||||||
|
_ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheCacheIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_mockCacheManager
|
||||||
|
.Verify(x => x.Add(It.IsAny<CacheItem<string>>()), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheResultIs(string expected)
|
||||||
|
{
|
||||||
|
_resultGet.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetFromTheCache()
|
||||||
|
{
|
||||||
|
_resultGet = _ocelotOcelotCacheManager.Get(_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheFollowingIsCached(string key, string value)
|
||||||
|
{
|
||||||
|
_key = key;
|
||||||
|
_value = value;
|
||||||
|
_mockCacheManager
|
||||||
|
.Setup(x => x.Get<string>(It.IsAny<string>()))
|
||||||
|
.Returns(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
139
test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs
Normal file
139
test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using CacheManager.Core;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.TestHost;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Cache;
|
||||||
|
using Ocelot.Cache.Middleware;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Configuration.Builder;
|
||||||
|
using Ocelot.DownstreamRouteFinder;
|
||||||
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Cache
|
||||||
|
{
|
||||||
|
public class OutputCacheMiddlewareTests
|
||||||
|
{
|
||||||
|
private readonly Mock<IOcelotCache<HttpResponseMessage>> _cacheManager;
|
||||||
|
private readonly Mock<IRequestScopedDataRepository> _scopedRepo;
|
||||||
|
|
||||||
|
private readonly string _url;
|
||||||
|
private readonly TestServer _server;
|
||||||
|
private readonly HttpClient _client;
|
||||||
|
private HttpResponseMessage _result;
|
||||||
|
private HttpResponseMessage _response;
|
||||||
|
|
||||||
|
public OutputCacheMiddlewareTests()
|
||||||
|
{
|
||||||
|
_cacheManager = new Mock<IOcelotCache<HttpResponseMessage>>();
|
||||||
|
_scopedRepo = new Mock<IRequestScopedDataRepository>();
|
||||||
|
_url = "http://localhost:51879";
|
||||||
|
var builder = new WebHostBuilder()
|
||||||
|
.ConfigureServices(x =>
|
||||||
|
{
|
||||||
|
x.AddLogging();
|
||||||
|
x.AddSingleton(_cacheManager.Object);
|
||||||
|
x.AddSingleton(_scopedRepo.Object);
|
||||||
|
})
|
||||||
|
.UseUrls(_url)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.UseUrls(_url)
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UseOutputCacheMiddleware();
|
||||||
|
});
|
||||||
|
|
||||||
|
_server = new TestServer(builder);
|
||||||
|
_client = _server.CreateClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_returned_cached_item_when_it_is_in_cache()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenThereIsACachedResponse(new HttpResponseMessage()))
|
||||||
|
.And(x => x.GivenTheDownstreamRouteIs())
|
||||||
|
.And(x => x.GivenThereIsADownstreamUrl())
|
||||||
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
|
.Then(x => x.ThenTheCacheGetIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_continue_with_pipeline_and_cache_response()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenResponseIsNotCached())
|
||||||
|
.And(x => x.GivenTheDownstreamRouteIs())
|
||||||
|
.And(x => x.GivenThereAreNoErrors())
|
||||||
|
.And(x => x.GivenThereIsADownstreamUrl())
|
||||||
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
|
.Then(x => x.ThenTheCacheAddIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheDownstreamRouteIs()
|
||||||
|
{
|
||||||
|
var reRoute = new ReRouteBuilder().WithIsCached(true).WithCacheOptions(new CacheOptions(100)).Build();
|
||||||
|
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), reRoute);
|
||||||
|
|
||||||
|
_scopedRepo
|
||||||
|
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
|
||||||
|
.Returns(new OkResponse<DownstreamRoute>(downstreamRoute));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereAreNoErrors()
|
||||||
|
{
|
||||||
|
_scopedRepo
|
||||||
|
.Setup(x => x.Get<bool>("OcelotMiddlewareError"))
|
||||||
|
.Returns(new OkResponse<bool>(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsADownstreamUrl()
|
||||||
|
{
|
||||||
|
_scopedRepo
|
||||||
|
.Setup(x => x.Get<string>("DownstreamUrl"))
|
||||||
|
.Returns(new OkResponse<string>("anything"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheCacheGetIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_cacheManager
|
||||||
|
.Verify(x => x.Get(It.IsAny<string>()), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheCacheAddIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_cacheManager
|
||||||
|
.Verify(x => x.Add(It.IsAny<string>(), It.IsAny<HttpResponseMessage>(), It.IsAny<TimeSpan>()), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenResponseIsNotCached()
|
||||||
|
{
|
||||||
|
_scopedRepo
|
||||||
|
.Setup(x => x.Get<HttpResponseMessage>("HttpResponseMessage"))
|
||||||
|
.Returns(new OkResponse<HttpResponseMessage>(new HttpResponseMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsACachedResponse(HttpResponseMessage response)
|
||||||
|
{
|
||||||
|
_response = response;
|
||||||
|
_cacheManager
|
||||||
|
.Setup(x => x.Get(It.IsAny<string>()))
|
||||||
|
.Returns(_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenICallTheMiddleware()
|
||||||
|
{
|
||||||
|
_result = _client.GetAsync(_url).Result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Http;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.TestHost;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Moq;
|
|
||||||
using Ocelot.Middleware;
|
|
||||||
using Ocelot.RequestId.Provider;
|
|
||||||
using TestStack.BDDfy;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Ocelot.UnitTests.Errors
|
|
||||||
{
|
|
||||||
public class GobalErrorHandlerTests
|
|
||||||
{
|
|
||||||
private readonly Mock<ILoggerFactory> _loggerFactory;
|
|
||||||
private readonly Mock<ILogger<ExceptionHandlerMiddleware>> _logger;
|
|
||||||
private readonly Mock<IRequestIdProvider> _requestIdProvider;
|
|
||||||
private readonly string _url;
|
|
||||||
private readonly TestServer _server;
|
|
||||||
private readonly HttpClient _client;
|
|
||||||
private HttpResponseMessage _result;
|
|
||||||
|
|
||||||
public GobalErrorHandlerTests()
|
|
||||||
{
|
|
||||||
_url = "http://localhost:51879";
|
|
||||||
_logger = new Mock<ILogger<ExceptionHandlerMiddleware>>();
|
|
||||||
_loggerFactory = new Mock<ILoggerFactory>();
|
|
||||||
_requestIdProvider = new Mock<IRequestIdProvider>();
|
|
||||||
var builder = new WebHostBuilder()
|
|
||||||
.ConfigureServices(x =>
|
|
||||||
{
|
|
||||||
x.AddSingleton(_requestIdProvider.Object);
|
|
||||||
x.AddSingleton(_loggerFactory.Object);
|
|
||||||
})
|
|
||||||
.UseUrls(_url)
|
|
||||||
.UseKestrel()
|
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
|
||||||
.UseIISIntegration()
|
|
||||||
.UseUrls(_url)
|
|
||||||
.Configure(app =>
|
|
||||||
{
|
|
||||||
app.UseExceptionHandlerMiddleware();
|
|
||||||
|
|
||||||
app.Run(x =>
|
|
||||||
{
|
|
||||||
throw new Exception("BLAM");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
_loggerFactory
|
|
||||||
.Setup(x => x.CreateLogger<ExceptionHandlerMiddleware>())
|
|
||||||
.Returns(_logger.Object);
|
|
||||||
|
|
||||||
_server = new TestServer(builder);
|
|
||||||
_client = _server.CreateClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void should_catch_exception_and_log()
|
|
||||||
{
|
|
||||||
this.When(x => x.WhenICallTheMiddleware())
|
|
||||||
.And(x => x.TheLoggerIsCalledCorrectly())
|
|
||||||
.BDDfy();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TheLoggerIsCalledCorrectly()
|
|
||||||
{
|
|
||||||
_logger
|
|
||||||
.Verify(x => x.LogError(It.IsAny<EventId>(), It.IsAny<string>(), It.IsAny<Exception>()), Times.Once);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WhenICallTheMiddleware()
|
|
||||||
{
|
|
||||||
_result = _client.GetAsync(_url).Result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
@ -4,7 +4,6 @@ namespace Ocelot.UnitTests.Infrastructure
|
|||||||
{
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Errors;
|
|
||||||
using Ocelot.Infrastructure.Claims.Parser;
|
using Ocelot.Infrastructure.Claims.Parser;
|
||||||
using Responses;
|
using Responses;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
|
@ -27,19 +27,16 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
private HttpResponseMessage _result;
|
private HttpResponseMessage _result;
|
||||||
private OkResponse<HttpResponseMessage> _response;
|
private OkResponse<HttpResponseMessage> _response;
|
||||||
private OkResponse<Ocelot.Request.Request> _request;
|
private OkResponse<Ocelot.Request.Request> _request;
|
||||||
private readonly Mock<IHttpResponder> _responder;
|
|
||||||
|
|
||||||
public HttpRequesterMiddlewareTests()
|
public HttpRequesterMiddlewareTests()
|
||||||
{
|
{
|
||||||
_url = "http://localhost:51879";
|
_url = "http://localhost:51879";
|
||||||
_requester = new Mock<IHttpRequester>();
|
_requester = new Mock<IHttpRequester>();
|
||||||
_scopedRepository = new Mock<IRequestScopedDataRepository>();
|
_scopedRepository = new Mock<IRequestScopedDataRepository>();
|
||||||
_responder = new Mock<IHttpResponder>();
|
|
||||||
|
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.ConfigureServices(x =>
|
.ConfigureServices(x =>
|
||||||
{
|
{
|
||||||
x.AddSingleton(_responder.Object);
|
|
||||||
x.AddSingleton(_requester.Object);
|
x.AddSingleton(_requester.Object);
|
||||||
x.AddSingleton(_scopedRepository.Object);
|
x.AddSingleton(_scopedRepository.Object);
|
||||||
})
|
})
|
||||||
@ -62,9 +59,9 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),new CookieContainer())))
|
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),new CookieContainer())))
|
||||||
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
|
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
|
||||||
.And(x => x.GivenTheResponderReturns())
|
.And(x => x.GivenTheScopedRepoReturns())
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenTheResponderIsCalledCorrectly())
|
.Then(x => x.ThenTheScopedRepoIsCalledCorrectly())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,17 +73,17 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
.ReturnsAsync(_response);
|
.ReturnsAsync(_response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheResponderReturns()
|
private void GivenTheScopedRepoReturns()
|
||||||
{
|
{
|
||||||
_responder
|
_scopedRepository
|
||||||
.Setup(x => x.SetResponseOnHttpContext(It.IsAny<HttpContext>(), _response.Data))
|
.Setup(x => x.Add(It.IsAny<string>(), _response.Data))
|
||||||
.ReturnsAsync(new OkResponse());
|
.Returns(new OkResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheResponderIsCalledCorrectly()
|
private void ThenTheScopedRepoIsCalledCorrectly()
|
||||||
{
|
{
|
||||||
_responder
|
_scopedRepository
|
||||||
.Verify(x => x.SetResponseOnHttpContext(It.IsAny<HttpContext>(), _response.Data), Times.Once());
|
.Verify(x => x.Add("HttpResponseMessage", _response.Data), Times.Once());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WhenICallTheMiddleware()
|
private void WhenICallTheMiddleware()
|
||||||
|
@ -59,11 +59,19 @@ namespace Ocelot.UnitTests.Responder
|
|||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheHttpResponseMessageIs(new HttpResponseMessage()))
|
this.Given(x => x.GivenTheHttpResponseMessageIs(new HttpResponseMessage()))
|
||||||
.And(x => x.GivenThereAreNoPipelineErrors())
|
.And(x => x.GivenThereAreNoPipelineErrors())
|
||||||
|
.And(x => x.GivenTheResponderReturns())
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenThereAreNoErrors())
|
.Then(x => x.ThenThereAreNoErrors())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GivenTheResponderReturns()
|
||||||
|
{
|
||||||
|
_responder
|
||||||
|
.Setup(x => x.SetResponseOnHttpContext(It.IsAny<HttpContext>(), It.IsAny<HttpResponseMessage>()))
|
||||||
|
.ReturnsAsync(new OkResponse());
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenThereAreNoPipelineErrors()
|
private void GivenThereAreNoPipelineErrors()
|
||||||
{
|
{
|
||||||
_scopedRepository
|
_scopedRepository
|
||||||
|
Loading…
x
Reference in New Issue
Block a user