mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
commit
247403f72a
@ -219,6 +219,6 @@ If you want to ignore SSL warnings / errors set the following in your ReRoute co
|
|||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
"DangerousAcceptAnyServerCertificateValidator": false
|
"DangerousAcceptAnyServerCertificateValidator": true
|
||||||
|
|
||||||
I don't recommend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can.
|
I don't recommend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can.
|
||||||
|
@ -18,7 +18,7 @@ You must choose in your configuration which load balancer to use.
|
|||||||
Configuration
|
Configuration
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
The following shows how to set up multiple downstream services for a ReRoute using ocelot.json and then select the LeadConnection load balancer. This is the simplest way to get load balancing set up.
|
The following shows how to set up multiple downstream services for a ReRoute using ocelot.json and then select the LeastConnection load balancer. This is the simplest way to get load balancing set up.
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ Ocelot supports one QoS capability at the current time. You can set on a per ReR
|
|||||||
want to use a circuit breaker when making requests to a downstream service. This uses an awesome
|
want to use a circuit breaker when making requests to a downstream service. This uses an awesome
|
||||||
.NET library called Polly check them out `here <https://github.com/App-vNext/Polly>`_.
|
.NET library called Polly check them out `here <https://github.com/App-vNext/Polly>`_.
|
||||||
|
|
||||||
The first thing you need to do if you want to use the administration API is bring in the relavent NuGet package..
|
The first thing you need to do if you want to use the administration API is bring in the relevant NuGet package..
|
||||||
|
|
||||||
``Install-Package Ocelot.Provider.Polly``
|
``Install-Package Ocelot.Provider.Polly``
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ The following is a very basic ocelot.json. It won't do anything but should get O
|
|||||||
The most important thing to note here is BaseUrl. Ocelot needs to know the URL it is running under
|
The most important thing to note here is BaseUrl. Ocelot needs to know the URL it is running under
|
||||||
in order to do Header find & replace and for certain administration configurations. When setting this URL it should be the external URL that clients will see Ocelot running on e.g. If you are running containers Ocelot might run on the url http://123.12.1.1:6543 but has something like nginx in front of it responding on https://api.mybusiness.com. In this case the Ocelot base url should be https://api.mybusiness.com.
|
in order to do Header find & replace and for certain administration configurations. When setting this URL it should be the external URL that clients will see Ocelot running on e.g. If you are running containers Ocelot might run on the url http://123.12.1.1:6543 but has something like nginx in front of it responding on https://api.mybusiness.com. In this case the Ocelot base url should be https://api.mybusiness.com.
|
||||||
|
|
||||||
If for some reason you are using containers and do want Ocelot to respond to client on http://123.12.1.1:6543
|
If you are using containers and require Ocelot to respond to clients on http://123.12.1.1:6543
|
||||||
then you can do this but if you are deploying multiple Ocelot's you will probably want to pass this on the command line in some kind of script. Hopefully whatever scheduler you are using can pass the IP.
|
then you can do this, however if you are deploying multiple Ocelot's you will probably want to pass this on the command line in some kind of script. Hopefully whatever scheduler you are using can pass the IP.
|
||||||
|
|
||||||
**Program**
|
**Program**
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
|
||||||
public class ReRouteBuilder
|
public class ReRouteBuilder
|
||||||
{
|
{
|
||||||
@ -11,11 +12,13 @@
|
|||||||
private List<HttpMethod> _upstreamHttpMethod;
|
private List<HttpMethod> _upstreamHttpMethod;
|
||||||
private string _upstreamHost;
|
private string _upstreamHost;
|
||||||
private List<DownstreamReRoute> _downstreamReRoutes;
|
private List<DownstreamReRoute> _downstreamReRoutes;
|
||||||
|
private List<AggregateReRouteConfig> _downstreamReRoutesConfig;
|
||||||
private string _aggregator;
|
private string _aggregator;
|
||||||
|
|
||||||
public ReRouteBuilder()
|
public ReRouteBuilder()
|
||||||
{
|
{
|
||||||
_downstreamReRoutes = new List<DownstreamReRoute>();
|
_downstreamReRoutes = new List<DownstreamReRoute>();
|
||||||
|
_downstreamReRoutesConfig = new List<AggregateReRouteConfig>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReRouteBuilder WithDownstreamReRoute(DownstreamReRoute value)
|
public ReRouteBuilder WithDownstreamReRoute(DownstreamReRoute value)
|
||||||
@ -48,6 +51,12 @@
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReRouteBuilder WithAggregateReRouteConfig(List<AggregateReRouteConfig> aggregateReRouteConfigs)
|
||||||
|
{
|
||||||
|
_downstreamReRoutesConfig = aggregateReRouteConfigs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ReRouteBuilder WithAggregator(string aggregator)
|
public ReRouteBuilder WithAggregator(string aggregator)
|
||||||
{
|
{
|
||||||
_aggregator = aggregator;
|
_aggregator = aggregator;
|
||||||
@ -58,6 +67,7 @@
|
|||||||
{
|
{
|
||||||
return new ReRoute(
|
return new ReRoute(
|
||||||
_downstreamReRoutes,
|
_downstreamReRoutes,
|
||||||
|
_downstreamReRoutesConfig,
|
||||||
_upstreamHttpMethod,
|
_upstreamHttpMethod,
|
||||||
_upstreamTemplatePattern,
|
_upstreamTemplatePattern,
|
||||||
_upstreamHost,
|
_upstreamHost,
|
||||||
|
@ -24,22 +24,27 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
private ReRoute SetUpAggregateReRoute(IEnumerable<ReRoute> reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
|
private ReRoute SetUpAggregateReRoute(IEnumerable<ReRoute> reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
|
||||||
{
|
{
|
||||||
var applicableReRoutes = reRoutes
|
var applicableReRoutes = new List<DownstreamReRoute>();
|
||||||
.SelectMany(x => x.DownstreamReRoute)
|
var allReRoutes = reRoutes.SelectMany(x => x.DownstreamReRoute);
|
||||||
.Where(r => aggregateReRoute.ReRouteKeys.Contains(r.Key))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (applicableReRoutes.Count != aggregateReRoute.ReRouteKeys.Count)
|
foreach (var reRouteKey in aggregateReRoute.ReRouteKeys)
|
||||||
|
{
|
||||||
|
var selec = allReRoutes.FirstOrDefault(q => q.Key == reRouteKey);
|
||||||
|
if (selec == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applicableReRoutes.Add(selec);
|
||||||
|
}
|
||||||
|
|
||||||
var upstreamTemplatePattern = _creator.Create(aggregateReRoute);
|
var upstreamTemplatePattern = _creator.Create(aggregateReRoute);
|
||||||
|
|
||||||
var reRoute = new ReRouteBuilder()
|
var reRoute = new ReRouteBuilder()
|
||||||
.WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod)
|
.WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod)
|
||||||
.WithUpstreamPathTemplate(upstreamTemplatePattern)
|
.WithUpstreamPathTemplate(upstreamTemplatePattern)
|
||||||
.WithDownstreamReRoutes(applicableReRoutes)
|
.WithDownstreamReRoutes(applicableReRoutes)
|
||||||
|
.WithAggregateReRouteConfig(aggregateReRoute.ReRouteKeysConfig)
|
||||||
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
|
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
|
||||||
.WithAggregator(aggregateReRoute.Aggregator)
|
.WithAggregator(aggregateReRoute.Aggregator)
|
||||||
.Build();
|
.Build();
|
||||||
|
13
src/Ocelot/Configuration/File/AggregateReRouteConfig.cs
Normal file
13
src/Ocelot/Configuration/File/AggregateReRouteConfig.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.File
|
||||||
|
{
|
||||||
|
public class AggregateReRouteConfig
|
||||||
|
{
|
||||||
|
public string ReRouteKey { get; set; }
|
||||||
|
public string Parameter { get; set; }
|
||||||
|
public string JsonPath { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ namespace Ocelot.Configuration.File
|
|||||||
public class FileAggregateReRoute : IReRoute
|
public class FileAggregateReRoute : IReRoute
|
||||||
{
|
{
|
||||||
public List<string> ReRouteKeys { get;set; }
|
public List<string> ReRouteKeys { get;set; }
|
||||||
|
public List<AggregateReRouteConfig> ReRouteKeysConfig { get;set; }
|
||||||
public string UpstreamPathTemplate { get;set; }
|
public string UpstreamPathTemplate { get;set; }
|
||||||
public string UpstreamHost { get; set; }
|
public string UpstreamHost { get; set; }
|
||||||
public bool ReRouteIsCaseSensitive { get; set; }
|
public bool ReRouteIsCaseSensitive { get; set; }
|
||||||
|
@ -6,7 +6,7 @@ namespace Ocelot.Configuration
|
|||||||
{
|
{
|
||||||
public LoadBalancerOptions(string type, string key, int expiryInMs)
|
public LoadBalancerOptions(string type, string key, int expiryInMs)
|
||||||
{
|
{
|
||||||
Type = type ?? nameof(NoLoadBalancer);
|
Type = string.IsNullOrWhiteSpace(type) ? nameof(NoLoadBalancer) : type;
|
||||||
Key = key;
|
Key = key;
|
||||||
ExpiryInMs = expiryInMs;
|
ExpiryInMs = expiryInMs;
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
{
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
public class ReRoute
|
public class ReRoute
|
||||||
{
|
{
|
||||||
public ReRoute(List<DownstreamReRoute> downstreamReRoute,
|
public ReRoute(List<DownstreamReRoute> downstreamReRoute,
|
||||||
|
List<AggregateReRouteConfig> downstreamReRouteConfig,
|
||||||
List<HttpMethod> upstreamHttpMethod,
|
List<HttpMethod> upstreamHttpMethod,
|
||||||
UpstreamPathTemplate upstreamTemplatePattern,
|
UpstreamPathTemplate upstreamTemplatePattern,
|
||||||
string upstreamHost,
|
string upstreamHost,
|
||||||
@ -14,6 +16,7 @@
|
|||||||
{
|
{
|
||||||
UpstreamHost = upstreamHost;
|
UpstreamHost = upstreamHost;
|
||||||
DownstreamReRoute = downstreamReRoute;
|
DownstreamReRoute = downstreamReRoute;
|
||||||
|
DownstreamReRouteConfig = downstreamReRouteConfig;
|
||||||
UpstreamHttpMethod = upstreamHttpMethod;
|
UpstreamHttpMethod = upstreamHttpMethod;
|
||||||
UpstreamTemplatePattern = upstreamTemplatePattern;
|
UpstreamTemplatePattern = upstreamTemplatePattern;
|
||||||
Aggregator = aggregator;
|
Aggregator = aggregator;
|
||||||
@ -23,6 +26,7 @@
|
|||||||
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
|
public List<HttpMethod> UpstreamHttpMethod { get; private set; }
|
||||||
public string UpstreamHost { get; private set; }
|
public string UpstreamHost { get; private set; }
|
||||||
public List<DownstreamReRoute> DownstreamReRoute { get; private set; }
|
public List<DownstreamReRoute> DownstreamReRoute { get; private set; }
|
||||||
|
public List<AggregateReRouteConfig> DownstreamReRouteConfig { get; private set; }
|
||||||
public string Aggregator {get; private set;}
|
public string Aggregator {get; private set;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,12 @@ namespace Ocelot.Middleware.Multiplexer
|
|||||||
public class InMemoryResponseAggregatorFactory : IResponseAggregatorFactory
|
public class InMemoryResponseAggregatorFactory : IResponseAggregatorFactory
|
||||||
{
|
{
|
||||||
private readonly UserDefinedResponseAggregator _userDefined;
|
private readonly UserDefinedResponseAggregator _userDefined;
|
||||||
private readonly SimpleJsonResponseAggregator _simple;
|
private readonly IResponseAggregator _simple;
|
||||||
|
|
||||||
public InMemoryResponseAggregatorFactory(IDefinedAggregatorProvider provider)
|
public InMemoryResponseAggregatorFactory(IDefinedAggregatorProvider provider, IResponseAggregator responseAggregator)
|
||||||
{
|
{
|
||||||
_userDefined = new UserDefinedResponseAggregator(provider);
|
_userDefined = new UserDefinedResponseAggregator(provider);
|
||||||
_simple = new SimpleJsonResponseAggregator();
|
_simple = responseAggregator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IResponseAggregator Get(ReRoute reRoute)
|
public IResponseAggregator Get(ReRoute reRoute)
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
|
|
||||||
namespace Ocelot.Middleware.Multiplexer
|
namespace Ocelot.Middleware.Multiplexer
|
||||||
{
|
{
|
||||||
@ -15,6 +17,9 @@ namespace Ocelot.Middleware.Multiplexer
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next)
|
public async Task Multiplex(DownstreamContext context, ReRoute reRoute, OcelotRequestDelegate next)
|
||||||
|
{
|
||||||
|
var reRouteKeysConfigs = reRoute.DownstreamReRouteConfig;
|
||||||
|
if (reRouteKeysConfigs == null || !reRouteKeysConfigs.Any())
|
||||||
{
|
{
|
||||||
var tasks = new Task<DownstreamContext>[reRoute.DownstreamReRoute.Count];
|
var tasks = new Task<DownstreamContext>[reRoute.DownstreamReRoute.Count];
|
||||||
|
|
||||||
@ -42,6 +47,77 @@ namespace Ocelot.Middleware.Multiplexer
|
|||||||
|
|
||||||
await Map(reRoute, context, contexts);
|
await Map(reRoute, context, contexts);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var downstreamContextMain = new DownstreamContext(context.HttpContext)
|
||||||
|
{
|
||||||
|
TemplatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues,
|
||||||
|
Configuration = context.Configuration,
|
||||||
|
DownstreamReRoute = reRoute.DownstreamReRoute[0],
|
||||||
|
};
|
||||||
|
var mainResponse = await Fire(downstreamContextMain, next);
|
||||||
|
|
||||||
|
if (reRoute.DownstreamReRoute.Count == 1)
|
||||||
|
{
|
||||||
|
MapNotAggregate(context, new List<DownstreamContext>() { mainResponse });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tasks = new List<Task<DownstreamContext>>();
|
||||||
|
if (mainResponse.DownstreamResponse == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = await mainResponse.DownstreamResponse.Content.ReadAsStringAsync();
|
||||||
|
var jObject = Newtonsoft.Json.Linq.JToken.Parse(content);
|
||||||
|
|
||||||
|
for (var i = 1; i < reRoute.DownstreamReRoute.Count; i++)
|
||||||
|
{
|
||||||
|
var templatePlaceholderNameAndValues = context.TemplatePlaceholderNameAndValues;
|
||||||
|
var downstreamReRoute = reRoute.DownstreamReRoute[i];
|
||||||
|
var matchAdvancedAgg = reRouteKeysConfigs.FirstOrDefault(q => q.ReRouteKey == downstreamReRoute.Key);
|
||||||
|
if (matchAdvancedAgg != null)
|
||||||
|
{
|
||||||
|
var values = jObject.SelectTokens(matchAdvancedAgg.JsonPath).Select(s => s.ToString()).Distinct().ToList();
|
||||||
|
|
||||||
|
foreach (var value in values)
|
||||||
|
{
|
||||||
|
var downstreamContext = new DownstreamContext(context.HttpContext)
|
||||||
|
{
|
||||||
|
TemplatePlaceholderNameAndValues = new List<PlaceholderNameAndValue>(templatePlaceholderNameAndValues),
|
||||||
|
Configuration = context.Configuration,
|
||||||
|
DownstreamReRoute = downstreamReRoute,
|
||||||
|
};
|
||||||
|
downstreamContext.TemplatePlaceholderNameAndValues.Add(new PlaceholderNameAndValue("{" + matchAdvancedAgg.Parameter + "}", value.ToString()));
|
||||||
|
tasks.Add(Fire(downstreamContext, next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var downstreamContext = new DownstreamContext(context.HttpContext)
|
||||||
|
{
|
||||||
|
TemplatePlaceholderNameAndValues = new List<PlaceholderNameAndValue>(templatePlaceholderNameAndValues),
|
||||||
|
Configuration = context.Configuration,
|
||||||
|
DownstreamReRoute = downstreamReRoute,
|
||||||
|
};
|
||||||
|
tasks.Add(Fire(downstreamContext, next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
var contexts = new List<DownstreamContext>() { mainResponse };
|
||||||
|
|
||||||
|
foreach (var task in tasks)
|
||||||
|
{
|
||||||
|
var finished = await task;
|
||||||
|
contexts.Add(finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Map(reRoute, context, contexts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Map(ReRoute reRoute, DownstreamContext context, List<DownstreamContext> contexts)
|
private async Task Map(ReRoute reRoute, DownstreamContext context, List<DownstreamContext> contexts)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
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;
|
||||||
@ -21,19 +22,54 @@ namespace Ocelot.Middleware.Multiplexer
|
|||||||
|
|
||||||
contentBuilder.Append("{");
|
contentBuilder.Append("{");
|
||||||
|
|
||||||
for (var i = 0; i < downstreamContexts.Count; i++)
|
var responseKeys = downstreamContexts.Select(s => s.DownstreamReRoute.Key).Distinct().ToList();
|
||||||
|
|
||||||
|
for (var k = 0; k < responseKeys.Count; k++)
|
||||||
{
|
{
|
||||||
if (downstreamContexts[i].IsError)
|
var contexts = downstreamContexts.Where(w => w.DownstreamReRoute.Key == responseKeys[k]).ToList();
|
||||||
|
if (contexts.Count == 1)
|
||||||
{
|
{
|
||||||
MapAggregateError(originalContext, downstreamContexts, i);
|
if (contexts[0].IsError)
|
||||||
|
{
|
||||||
|
MapAggregateError(originalContext, contexts[0]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var content = await downstreamContexts[i].DownstreamResponse.Content.ReadAsStringAsync();
|
var content = await contexts[0].DownstreamResponse.Content.ReadAsStringAsync();
|
||||||
|
contentBuilder.Append($"\"{responseKeys[k]}\":{content}");
|
||||||
|
|
||||||
contentBuilder.Append($"\"{downstreamContexts[i].DownstreamReRoute.Key}\":{content}");
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
contentBuilder.Append($"\"{responseKeys[k]}\":");
|
||||||
|
contentBuilder.Append("[");
|
||||||
|
|
||||||
if (i + 1 < downstreamContexts.Count)
|
for (var i = 0; i < contexts.Count; i++)
|
||||||
|
{
|
||||||
|
if (contexts[i].IsError)
|
||||||
|
{
|
||||||
|
MapAggregateError(originalContext, contexts[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = await contexts[i].DownstreamResponse.Content.ReadAsStringAsync();
|
||||||
|
if (string.IsNullOrWhiteSpace(content))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
contentBuilder.Append($"{content}");
|
||||||
|
|
||||||
|
if (i + 1 < contexts.Count)
|
||||||
|
{
|
||||||
|
contentBuilder.Append(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentBuilder.Append("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k + 1 < responseKeys.Count)
|
||||||
{
|
{
|
||||||
contentBuilder.Append(",");
|
contentBuilder.Append(",");
|
||||||
}
|
}
|
||||||
@ -49,10 +85,10 @@ namespace Ocelot.Middleware.Multiplexer
|
|||||||
originalContext.DownstreamResponse = new DownstreamResponse(stringContent, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "cannot return from aggregate..which reason phrase would you use?");
|
originalContext.DownstreamResponse = new DownstreamResponse(stringContent, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "cannot return from aggregate..which reason phrase would you use?");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MapAggregateError(DownstreamContext originalContext, List<DownstreamContext> downstreamContexts, int i)
|
private static void MapAggregateError(DownstreamContext originalContext, DownstreamContext downstreamContext)
|
||||||
{
|
{
|
||||||
originalContext.Errors.AddRange(downstreamContexts[i].Errors);
|
originalContext.Errors.AddRange(downstreamContext.Errors);
|
||||||
originalContext.DownstreamResponse = downstreamContexts[i].DownstreamResponse;
|
originalContext.DownstreamResponse = downstreamContext.DownstreamResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ namespace Ocelot.Request.Middleware
|
|||||||
Port = Port,
|
Port = Port,
|
||||||
Host = Host,
|
Host = Host,
|
||||||
Path = AbsolutePath,
|
Path = AbsolutePath,
|
||||||
Query = Query,
|
Query = RemoveLeadingQuestionMark(Query),
|
||||||
Scheme = Scheme
|
Scheme = Scheme
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ namespace Ocelot.Request.Middleware
|
|||||||
Port = Port,
|
Port = Port,
|
||||||
Host = Host,
|
Host = Host,
|
||||||
Path = AbsolutePath,
|
Path = AbsolutePath,
|
||||||
Query = Query,
|
Query = RemoveLeadingQuestionMark(Query),
|
||||||
Scheme = Scheme
|
Scheme = Scheme
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,5 +70,15 @@ namespace Ocelot.Request.Middleware
|
|||||||
{
|
{
|
||||||
return ToUri();
|
return ToUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string RemoveLeadingQuestionMark(string query)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(query) && query.StartsWith("?"))
|
||||||
|
{
|
||||||
|
return query.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,15 @@ namespace Ocelot.Responder
|
|||||||
AddHeaderIfDoesntExist(context, httpResponseHeader);
|
AddHeaderIfDoesntExist(context, httpResponseHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetStatusCode(context, (int)response.StatusCode);
|
||||||
|
|
||||||
|
context.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = response.ReasonPhrase;
|
||||||
|
|
||||||
|
if (response.Content is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var httpResponseHeader in response.Content.Headers)
|
foreach (var httpResponseHeader in response.Content.Headers)
|
||||||
{
|
{
|
||||||
AddHeaderIfDoesntExist(context, new Header(httpResponseHeader.Key, httpResponseHeader.Value));
|
AddHeaderIfDoesntExist(context, new Header(httpResponseHeader.Key, httpResponseHeader.Value));
|
||||||
@ -44,10 +53,6 @@ namespace Ocelot.Responder
|
|||||||
AddHeaderIfDoesntExist(context, new Header("Content-Length", new []{ response.Content.Headers.ContentLength.ToString() }) );
|
AddHeaderIfDoesntExist(context, new Header("Content-Length", new []{ response.Content.Headers.ContentLength.ToString() }) );
|
||||||
}
|
}
|
||||||
|
|
||||||
SetStatusCode(context, (int)response.StatusCode);
|
|
||||||
|
|
||||||
context.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = response.ReasonPhrase;
|
|
||||||
|
|
||||||
using(content)
|
using(content)
|
||||||
{
|
{
|
||||||
if (response.StatusCode != HttpStatusCode.NotModified && context.Response.ContentLength != 0)
|
if (response.StatusCode != HttpStatusCode.NotModified && context.Response.ContentLength != 0)
|
||||||
|
@ -141,6 +141,100 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_with_advanced_aggregate_configs()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51889,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/Comments",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "Comments"
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/users/{userId}",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51890,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/UserDetails",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "UserDetails"
|
||||||
|
},
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/posts/{postId}",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51887,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/PostDetails",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
Key = "PostDetails"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Aggregates = new List<FileAggregateReRoute>
|
||||||
|
{
|
||||||
|
new FileAggregateReRoute
|
||||||
|
{
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHost = "localhost",
|
||||||
|
ReRouteKeys = new List<string>
|
||||||
|
{
|
||||||
|
"Comments",
|
||||||
|
"UserDetails",
|
||||||
|
"PostDetails"
|
||||||
|
},
|
||||||
|
ReRouteKeysConfig = new List<AggregateReRouteConfig>()
|
||||||
|
{
|
||||||
|
new AggregateReRouteConfig(){ReRouteKey = "UserDetails",JsonPath = "$[*].writerId",Parameter = "userId"},
|
||||||
|
new AggregateReRouteConfig(){ReRouteKey = "PostDetails",JsonPath = "$[*].postId",Parameter = "postId"}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var userDetailsResponseContent = @"{""id"":1,""firstName"":""abolfazl"",""lastName"":""rajabpour""}";
|
||||||
|
var postDetailsResponseContent = @"{""id"":1,""title"":""post1""}";
|
||||||
|
var commentsResponseContent = @"[{""id"":1,""writerId"":1,""postId"":2,""text"":""text1""},{""id"":2,""writerId"":1,""postId"":2,""text"":""text2""}]";
|
||||||
|
|
||||||
|
var expected = "{\"Comments\":" + commentsResponseContent + ",\"UserDetails\":" + userDetailsResponseContent + ",\"PostDetails\":" + postDetailsResponseContent + "}";
|
||||||
|
|
||||||
|
this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51889", "/", 200, commentsResponseContent))
|
||||||
|
.Given(x => x.GivenServiceTwoIsRunning("http://localhost:51890", "/users/1", 200, userDetailsResponseContent))
|
||||||
|
.Given(x => x.GivenServiceTwoIsRunning("http://localhost:51887", "/posts/2", 200, postDetailsResponseContent))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe(expected))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_200_with_simple_url_user_defined_aggregate()
|
public void should_return_response_200_with_simple_url_user_defined_aggregate()
|
||||||
{
|
{
|
||||||
@ -189,8 +283,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
UpstreamHost = "localhost",
|
UpstreamHost = "localhost",
|
||||||
ReRouteKeys = new List<string>
|
ReRouteKeys = new List<string>
|
||||||
{
|
{
|
||||||
"Tom",
|
"Laura",
|
||||||
"Laura"
|
"Tom"
|
||||||
},
|
},
|
||||||
Aggregator = "FakeDefinedAggregator"
|
Aggregator = "FakeDefinedAggregator"
|
||||||
}
|
}
|
||||||
@ -258,8 +352,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
UpstreamHost = "localhost",
|
UpstreamHost = "localhost",
|
||||||
ReRouteKeys = new List<string>
|
ReRouteKeys = new List<string>
|
||||||
{
|
{
|
||||||
"Tom",
|
"Laura",
|
||||||
"Laura"
|
"Tom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,8 +420,9 @@ namespace Ocelot.AcceptanceTests
|
|||||||
UpstreamHost = "localhost",
|
UpstreamHost = "localhost",
|
||||||
ReRouteKeys = new List<string>
|
ReRouteKeys = new List<string>
|
||||||
{
|
{
|
||||||
"Tom",
|
"Laura",
|
||||||
"Laura"
|
"Tom"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -394,8 +489,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
UpstreamHost = "localhost",
|
UpstreamHost = "localhost",
|
||||||
ReRouteKeys = new List<string>
|
ReRouteKeys = new List<string>
|
||||||
{
|
{
|
||||||
"Tom",
|
"Laura",
|
||||||
"Laura"
|
"Tom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -462,8 +557,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
UpstreamHost = "localhost",
|
UpstreamHost = "localhost",
|
||||||
ReRouteKeys = new List<string>
|
ReRouteKeys = new List<string>
|
||||||
{
|
{
|
||||||
"Tom",
|
"Laura",
|
||||||
"Laura"
|
"Tom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,8 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
public ResponseAggregatorFactoryTests()
|
public ResponseAggregatorFactoryTests()
|
||||||
{
|
{
|
||||||
_provider = new Mock<IDefinedAggregatorProvider>();
|
_provider = new Mock<IDefinedAggregatorProvider>();
|
||||||
_factory = new InMemoryResponseAggregatorFactory(_provider.Object);
|
_aggregator = new SimpleJsonResponseAggregator();
|
||||||
|
_factory = new InMemoryResponseAggregatorFactory(_provider.Object, _aggregator);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -7,11 +7,11 @@ using Castle.Components.DictionaryAdapter;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Configuration.Builder;
|
using Ocelot.Configuration.Builder;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Middleware.Multiplexer;
|
using Ocelot.Middleware.Multiplexer;
|
||||||
using Ocelot.Request.Middleware;
|
|
||||||
using Ocelot.UnitTests.Responder;
|
using Ocelot.UnitTests.Responder;
|
||||||
|
using Ocelot.Values;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -30,6 +30,58 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
_aggregator = new SimpleJsonResponseAggregator();
|
_aggregator = new SimpleJsonResponseAggregator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_aggregate_n_responses_and_set_response_content_on_upstream_context_withConfig()
|
||||||
|
{
|
||||||
|
var commentsDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("Comments").Build();
|
||||||
|
|
||||||
|
var userDetailsDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("UserDetails")
|
||||||
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("", 0, false, "/v1/users/{userId}"))
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var downstreamReRoutes = new List<DownstreamReRoute>
|
||||||
|
{
|
||||||
|
commentsDownstreamReRoute,
|
||||||
|
userDetailsDownstreamReRoute
|
||||||
|
};
|
||||||
|
|
||||||
|
var reRoute = new ReRouteBuilder()
|
||||||
|
.WithDownstreamReRoutes(downstreamReRoutes)
|
||||||
|
.WithAggregateReRouteConfig(new List<AggregateReRouteConfig>()
|
||||||
|
{
|
||||||
|
new AggregateReRouteConfig(){ReRouteKey = "UserDetails",JsonPath = "$[*].writerId",Parameter = "userId"}
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var commentsResponseContent = @"[{""id"":1,""writerId"":1,""postId"":1,""text"":""text1""},{""id"":2,""writerId"":2,""postId"":2,""text"":""text2""},{""id"":3,""writerId"":2,""postId"":1,""text"":""text21""}]";
|
||||||
|
var commentsDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
||||||
|
{
|
||||||
|
DownstreamResponse = new DownstreamResponse(new StringContent(commentsResponseContent, Encoding.UTF8, "application/json"), HttpStatusCode.OK, new EditableList<KeyValuePair<string, IEnumerable<string>>>(), "some reason"),
|
||||||
|
DownstreamReRoute = commentsDownstreamReRoute
|
||||||
|
};
|
||||||
|
|
||||||
|
var userDetailsResponseContent = @"[{""id"":1,""firstName"":""abolfazl"",""lastName"":""rajabpour""},{""id"":2,""firstName"":""reza"",""lastName"":""rezaei""}]";
|
||||||
|
var userDetailsDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
||||||
|
{
|
||||||
|
DownstreamResponse = new DownstreamResponse(new StringContent(userDetailsResponseContent, Encoding.UTF8, "application/json"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "some reason"),
|
||||||
|
DownstreamReRoute = userDetailsDownstreamReRoute
|
||||||
|
};
|
||||||
|
|
||||||
|
var downstreamContexts = new List<DownstreamContext> { commentsDownstreamContext, userDetailsDownstreamContext };
|
||||||
|
|
||||||
|
var expected = "{\"Comments\":" + commentsResponseContent + ",\"UserDetails\":" + userDetailsResponseContent + "}";
|
||||||
|
|
||||||
|
this.Given(x => GivenTheUpstreamContext(new DownstreamContext(new DefaultHttpContext())))
|
||||||
|
.And(x => GivenTheReRoute(reRoute))
|
||||||
|
.And(x => GivenTheDownstreamContext(downstreamContexts))
|
||||||
|
.When(x => WhenIAggregate())
|
||||||
|
.Then(x => ThenTheContentIs(expected))
|
||||||
|
.And(x => ThenTheContentTypeIs("application/json"))
|
||||||
|
.And(x => ThenTheReasonPhraseIs("cannot return from aggregate..which reason phrase would you use?"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_aggregate_n_responses_and_set_response_content_on_upstream_context()
|
public void should_aggregate_n_responses_and_set_response_content_on_upstream_context()
|
||||||
{
|
{
|
||||||
|
@ -39,6 +39,23 @@ namespace Ocelot.UnitTests.Responder
|
|||||||
header.ShouldBeEmpty();
|
header.ShouldBeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_ignore_content_if_null()
|
||||||
|
{
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
var response = new DownstreamResponse(null, HttpStatusCode.OK,
|
||||||
|
new List<KeyValuePair<string, IEnumerable<string>>>(), "some reason");
|
||||||
|
|
||||||
|
Should.NotThrow(() =>
|
||||||
|
{
|
||||||
|
_responder
|
||||||
|
.SetResponseOnHttpContext(httpContext, response)
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult()
|
||||||
|
;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_have_content_length()
|
public void should_have_content_length()
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user