mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
* #451 started implementing querystring support in templates * #451 ocelot.json back to normal and specified in docs query string wont work in upstream template * Revert "#451 ocelot.json back to normal and specified in docs query string wont work in upstream template" This reverts commit 563193f7b2f78bad6109484fe77f3c87de831005. * #451 ocelot.json back to normal and specified in docs query string wont work in upstream template
This commit is contained in:
parent
89c3887d36
commit
75f9a8f9be
@ -185,3 +185,34 @@ Dynamic Routing
|
|||||||
This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing
|
This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing
|
||||||
when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if
|
when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if
|
||||||
this sounds interesting to you.
|
this sounds interesting to you.
|
||||||
|
|
||||||
|
Query Strings
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Ocelot allow's you to specify a querystring as part of the DownstreamPathTemplate like the example below.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"ReRoutes": [
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
|
||||||
|
"UpstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
|
||||||
|
"UpstreamHttpMethod": [
|
||||||
|
"Get"
|
||||||
|
],
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 50110
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GlobalConfiguration": {
|
||||||
|
"UseServiceDiscovery": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
In this example Ocelot will use the value from the {unitId} in the upstream path template and add it to the downstream request as a query string parameter called unitId! Please note you cannot use query string parameters to match routes in the UpstreamPathTemplate.
|
||||||
|
@ -28,14 +28,14 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
|
|||||||
|
|
||||||
public async Task Invoke(DownstreamContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var dsPath = _replacer
|
var response = _replacer
|
||||||
.Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
.Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
||||||
|
|
||||||
if (dsPath.IsError)
|
if (response.IsError)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
|
Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error");
|
||||||
|
|
||||||
SetPipelineError(context, dsPath.Errors);
|
SetPipelineError(context, response.Errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,13 +43,26 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
|
|||||||
|
|
||||||
if (ServiceFabricRequest(context))
|
if (ServiceFabricRequest(context))
|
||||||
{
|
{
|
||||||
var pathAndQuery = CreateServiceFabricUri(context, dsPath);
|
var pathAndQuery = CreateServiceFabricUri(context, response);
|
||||||
context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
|
context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
|
||||||
context.DownstreamRequest.Query = pathAndQuery.query;
|
context.DownstreamRequest.Query = pathAndQuery.query;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.DownstreamRequest.AbsolutePath = dsPath.Data.Value;
|
var dsPath = response.Data;
|
||||||
|
|
||||||
|
if(ContainsQueryString(dsPath))
|
||||||
|
{
|
||||||
|
context.DownstreamRequest.AbsolutePath = GetPath(dsPath);
|
||||||
|
context.DownstreamRequest.Query = GetQueryString(dsPath);
|
||||||
|
|
||||||
|
// todo - do we need to add anything from the request query string onto the query from the
|
||||||
|
// templae?
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.DownstreamRequest.AbsolutePath = dsPath.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogDebug($"Downstream url is {context.DownstreamRequest}");
|
Logger.LogDebug($"Downstream url is {context.DownstreamRequest}");
|
||||||
@ -57,6 +70,21 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
|
|||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetPath(DownstreamPath dsPath)
|
||||||
|
{
|
||||||
|
return dsPath.Value.Substring(0, dsPath.Value.IndexOf("?"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetQueryString(DownstreamPath dsPath)
|
||||||
|
{
|
||||||
|
return dsPath.Value.Substring(dsPath.Value.IndexOf("?"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ContainsQueryString(DownstreamPath dsPath)
|
||||||
|
{
|
||||||
|
return dsPath.Value.Contains("?");
|
||||||
|
}
|
||||||
|
|
||||||
private (string path, string query) CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
|
private (string path, string query) CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
|
||||||
{
|
{
|
||||||
var query = context.DownstreamRequest.Query;
|
var query = context.DownstreamRequest.Query;
|
||||||
|
104
test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs
Normal file
104
test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.AcceptanceTests
|
||||||
|
{
|
||||||
|
public class RoutingWithQueryStringTests : IDisposable
|
||||||
|
{
|
||||||
|
private IWebHost _builder;
|
||||||
|
private readonly Steps _steps;
|
||||||
|
private string _downstreamPath;
|
||||||
|
|
||||||
|
public RoutingWithQueryStringTests()
|
||||||
|
{
|
||||||
|
_steps = new Steps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_response_200_with_query_string_template()
|
||||||
|
{
|
||||||
|
var subscriptionId = Guid.NewGuid().ToString();
|
||||||
|
var unitId = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 61879,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:61879", $"/api/subscriptions/{subscriptionId}/updates", $"?unitId={unitId}", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunning())
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/units/{subscriptionId}/{unitId}/updates"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string queryString, int statusCode, string responseBody)
|
||||||
|
{
|
||||||
|
_builder = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UsePathBase(basePath);
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
|
||||||
|
if(context.Request.PathBase.Value != basePath || context.Request.QueryString.Value != queryString)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 404;
|
||||||
|
await context.Response.WriteAsync("downstream path didnt match base path");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
await context.Response.WriteAsync(responseBody);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_builder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
|
||||||
|
{
|
||||||
|
_downstreamPath.ShouldBe(expectedDownstreamPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_builder?.Dispose();
|
||||||
|
_steps.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,345 +1,345 @@
|
|||||||
{
|
{
|
||||||
"ReRoutes": [
|
"ReRoutes": [
|
||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/profile",
|
"DownstreamPathTemplate": "/profile",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
"UpstreamPathTemplate": "/profile",
|
"UpstreamPathTemplate": "/profile",
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
"DownstreamHostAndPorts": [
|
"DownstreamHostAndPorts": [
|
||||||
{
|
{
|
||||||
"Host": "localhost",
|
"Host": "localhost",
|
||||||
"Port": 3000
|
"Port": 3000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"QoSOptions": {
|
||||||
|
"TimeoutValue": 360000
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"QoSOptions": {
|
|
||||||
"TimeoutValue": 360000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/v1/todo/",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"UpstreamPathTemplate": "/api/v1/todo/",
|
|
||||||
"UpstreamHttpMethod": [ "Get", "Post" ],
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "lxtodo.azurewebsites.net",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
|
|
||||||
],
|
|
||||||
"DownstreamHeaderTransform": {
|
|
||||||
"Location": "{DownstreamBaseUrl}, {BaseUrl}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/values",
|
|
||||||
"DownstreamScheme": "https",
|
|
||||||
"UpstreamPathTemplate": "/api/values",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "testapivalues.azurewebsites.net",
|
|
||||||
"Port": 443
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "localhost",
|
|
||||||
"Port": 52876
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/identityserverexample",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
},
|
||||||
"AuthenticationOptions": {
|
{
|
||||||
"AuthenticationProviderKey": "TestKey",
|
"DownstreamPathTemplate": "/api/v1/todo/",
|
||||||
"AllowedScopes": [
|
"DownstreamScheme": "http",
|
||||||
"openid",
|
"UpstreamPathTemplate": "/api/v1/todo/",
|
||||||
"offline_access"
|
"UpstreamHttpMethod": [ "Get", "Post" ],
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "lxtodo.azurewebsites.net",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
|
||||||
|
],
|
||||||
|
"DownstreamHeaderTransform": {
|
||||||
|
"Location": "{DownstreamBaseUrl}, {BaseUrl}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/values",
|
||||||
|
"DownstreamScheme": "https",
|
||||||
|
"UpstreamPathTemplate": "/api/values",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "testapivalues.azurewebsites.net",
|
||||||
|
"Port": 443
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"AddHeadersToRequest": {
|
{
|
||||||
"CustomerId": "Claims[CustomerId] > value",
|
"DownstreamPathTemplate": "/",
|
||||||
"LocationId": "Claims[LocationId] > value",
|
"DownstreamScheme": "http",
|
||||||
"UserType": "Claims[sub] > value[0] > |",
|
"DownstreamHostAndPorts": [
|
||||||
"UserId": "Claims[sub] > value[1] > |"
|
{
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 52876
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/identityserverexample",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"AuthenticationOptions": {
|
||||||
|
"AuthenticationProviderKey": "TestKey",
|
||||||
|
"AllowedScopes": [
|
||||||
|
"openid",
|
||||||
|
"offline_access"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"AddHeadersToRequest": {
|
||||||
|
"CustomerId": "Claims[CustomerId] > value",
|
||||||
|
"LocationId": "Claims[LocationId] > value",
|
||||||
|
"UserType": "Claims[sub] > value[0] > |",
|
||||||
|
"UserId": "Claims[sub] > value[1] > |"
|
||||||
|
},
|
||||||
|
"AddClaimsToRequest": {
|
||||||
|
"CustomerId": "Claims[CustomerId] > value",
|
||||||
|
"LocationId": "Claims[LocationId] > value",
|
||||||
|
"UserType": "Claims[sub] > value[0] > |",
|
||||||
|
"UserId": "Claims[sub] > value[1] > |"
|
||||||
|
},
|
||||||
|
"AddQueriesToRequest": {
|
||||||
|
"CustomerId": "Claims[CustomerId] > value",
|
||||||
|
"LocationId": "Claims[LocationId] > value",
|
||||||
|
"UserType": "Claims[sub] > value[0] > |",
|
||||||
|
"UserId": "Claims[sub] > value[1] > |"
|
||||||
|
},
|
||||||
|
"RouteClaimsRequirement": {
|
||||||
|
"UserType": "registered"
|
||||||
|
},
|
||||||
|
"RequestIdKey": "OcRequestId"
|
||||||
},
|
},
|
||||||
"AddClaimsToRequest": {
|
{
|
||||||
"CustomerId": "Claims[CustomerId] > value",
|
"DownstreamPathTemplate": "/posts",
|
||||||
"LocationId": "Claims[LocationId] > value",
|
"DownstreamScheme": "https",
|
||||||
"UserType": "Claims[sub] > value[0] > |",
|
"DownstreamHostAndPorts": [
|
||||||
"UserId": "Claims[sub] > value[1] > |"
|
{
|
||||||
},
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
"AddQueriesToRequest": {
|
"Port": 443
|
||||||
"CustomerId": "Claims[CustomerId] > value",
|
}
|
||||||
"LocationId": "Claims[LocationId] > value",
|
],
|
||||||
"UserType": "Claims[sub] > value[0] > |",
|
"UpstreamPathTemplate": "/posts",
|
||||||
"UserId": "Claims[sub] > value[1] > |"
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
},
|
"HttpHandlerOptions": {
|
||||||
"RouteClaimsRequirement": {
|
"AllowAutoRedirect": true,
|
||||||
"UserType": "registered"
|
"UseCookieContainer": true
|
||||||
},
|
},
|
||||||
"RequestIdKey": "OcRequestId"
|
"QoSOptions": {
|
||||||
},
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
{
|
"DurationOfBreak": 10,
|
||||||
"DownstreamPathTemplate": "/posts",
|
"TimeoutValue": 5000
|
||||||
"DownstreamScheme": "https",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 443
|
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/posts",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"HttpHandlerOptions": {
|
|
||||||
"AllowAutoRedirect": true,
|
|
||||||
"UseCookieContainer": true
|
|
||||||
},
|
},
|
||||||
"QoSOptions": {
|
{
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
"DurationOfBreak": 10,
|
"DownstreamScheme": "http",
|
||||||
"TimeoutValue": 5000
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
|
"RequestIdKey": "ReRouteRequestId",
|
||||||
|
"HttpHandlerOptions": {
|
||||||
|
"AllowAutoRedirect": true,
|
||||||
|
"UseCookieContainer": true,
|
||||||
|
"UseTracing": true,
|
||||||
|
"UseProxy": true
|
||||||
|
},
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts/{postId}/comments",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}/comments",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
|
"HttpHandlerOptions": {
|
||||||
|
"AllowAutoRedirect": true,
|
||||||
|
"UseCookieContainer": true,
|
||||||
|
"UseTracing": false
|
||||||
|
},
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/comments",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/comments",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/posts",
|
||||||
|
"UpstreamHttpMethod": [ "Post" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": [ "Put" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": [ "Patch" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": [ "Delete" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/products",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/products",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/products/{productId}",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/products",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/products",
|
||||||
|
"UpstreamHttpMethod": [ "Post" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/products/{productId}",
|
||||||
|
"UpstreamHttpMethod": [ "Put" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "jsonplaceholder.typicode.com",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/posts/",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ],
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "www.bbc.co.uk",
|
||||||
|
"Port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/bbc/",
|
||||||
|
"UpstreamHttpMethod": [ "Get" ]
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/posts/{postId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/posts/{postId}",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"RequestIdKey": "ReRouteRequestId",
|
|
||||||
"HttpHandlerOptions": {
|
|
||||||
"AllowAutoRedirect": true,
|
|
||||||
"UseCookieContainer": true,
|
|
||||||
"UseTracing": true,
|
|
||||||
"UseProxy": true
|
|
||||||
},
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/posts/{postId}/comments",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/posts/{postId}/comments",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"HttpHandlerOptions": {
|
|
||||||
"AllowAutoRedirect": true,
|
|
||||||
"UseCookieContainer": true,
|
|
||||||
"UseTracing": false
|
|
||||||
},
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/comments",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/comments",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/posts",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/posts",
|
|
||||||
"UpstreamHttpMethod": [ "Post" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/posts/{postId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/posts/{postId}",
|
|
||||||
"UpstreamHttpMethod": [ "Put" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/posts/{postId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/posts/{postId}",
|
|
||||||
"UpstreamHttpMethod": [ "Patch" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/posts/{postId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/posts/{postId}",
|
|
||||||
"UpstreamHttpMethod": [ "Delete" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/products",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/products",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/products/{productId}",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/products",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/products",
|
|
||||||
"UpstreamHttpMethod": [ "Post" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/products/{productId}",
|
|
||||||
"UpstreamHttpMethod": [ "Put" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/posts",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "jsonplaceholder.typicode.com",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/posts/",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ],
|
|
||||||
"QoSOptions": {
|
|
||||||
"ExceptionsAllowedBeforeBreaking": 3,
|
|
||||||
"DurationOfBreak": 10,
|
|
||||||
"TimeoutValue": 5000
|
|
||||||
},
|
|
||||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DownstreamPathTemplate": "/",
|
|
||||||
"DownstreamScheme": "http",
|
|
||||||
"DownstreamHostAndPorts": [
|
|
||||||
{
|
|
||||||
"Host": "www.bbc.co.uk",
|
|
||||||
"Port": 80
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"UpstreamPathTemplate": "/bbc/",
|
|
||||||
"UpstreamHttpMethod": [ "Get" ]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
"GlobalConfiguration": {
|
"GlobalConfiguration": {
|
||||||
"RequestIdKey": "ot-traceid"
|
"RequestIdKey": "ot-traceid"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
|
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
|
||||||
|
.And(x => ThenTheQueryStringIs("?q=123"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,5 +228,10 @@
|
|||||||
{
|
{
|
||||||
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);
|
_downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenTheQueryStringIs(string queryString)
|
||||||
|
{
|
||||||
|
_downstreamContext.DownstreamRequest.Query.ShouldBe(queryString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user