mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 22:30:50 +08:00 
			
		
		
		
	fixed problems where routes were not mathing
This commit is contained in:
		@@ -122,6 +122,8 @@ Ocelot's primary functionality is to take incomeing http requests and forward th
 | 
			
		||||
to a downstream service. At the moment in the form of another http request (in the future
 | 
			
		||||
this could be any transport mechanism.). 
 | 
			
		||||
 | 
			
		||||
Ocelot always adds a trailing slash to an UpstreamTemplate.
 | 
			
		||||
 | 
			
		||||
Ocelot's describes the routing of one request to another as a ReRoute. In order to get 
 | 
			
		||||
anything working in Ocelot you need to set up a ReRoute in the configuration.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
{
 | 
			
		||||
	"ReRoutes": [
 | 
			
		||||
		{
 | 
			
		||||
			# The url we are forwarding the request to
 | 
			
		||||
			"UpstreamTemplate": "/identityserverexample",
 | 
			
		||||
			# The path we are listening on for this re route
 | 
			
		||||
			# The url we are forwarding the request to, ocelot will not add a trailing slash
 | 
			
		||||
			"DownstreamTemplate": "http://somehost.com/identityserverexample",
 | 
			
		||||
			# The path we are listening on for this re route, Ocelot will add a trailing slash to
 | 
			
		||||
			# this property. Then when a request is made Ocelot makes sure a trailing slash is added
 | 
			
		||||
			# to that so everything matches
 | 
			
		||||
			"UpstreamTemplate": "/identityserverexample",
 | 
			
		||||
			# The method we are listening for on this re route
 | 
			
		||||
			"UpstreamHttpMethod": "Get",
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ using Ocelot.Configuration.File;
 | 
			
		||||
using Ocelot.Configuration.Parser;
 | 
			
		||||
using Ocelot.Configuration.Validator;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
using Ocelot.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Creator
 | 
			
		||||
{
 | 
			
		||||
@@ -90,7 +91,6 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
                ? globalConfiguration.RequestIdKey
 | 
			
		||||
                : reRoute.RequestIdKey;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (isAuthenticated)
 | 
			
		||||
            {
 | 
			
		||||
                var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
 | 
			
		||||
@@ -120,6 +120,8 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
        {
 | 
			
		||||
            var upstreamTemplate = reRoute.UpstreamTemplate;
 | 
			
		||||
 | 
			
		||||
            upstreamTemplate = upstreamTemplate.SetLastCharacterAs('/');
 | 
			
		||||
 | 
			
		||||
            var placeholders = new List<string>();
 | 
			
		||||
 | 
			
		||||
            for (var i = 0; i < upstreamTemplate.Length; i++)
 | 
			
		||||
@@ -138,9 +140,11 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
                upstreamTemplate = upstreamTemplate.Replace(placeholder, RegExMatchEverything);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return reRoute.ReRouteIsCaseSensitive 
 | 
			
		||||
            var route = reRoute.ReRouteIsCaseSensitive 
 | 
			
		||||
                ? $"{upstreamTemplate}{RegExMatchEndString}" 
 | 
			
		||||
                : $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
 | 
			
		||||
 | 
			
		||||
            return route;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ using Ocelot.DownstreamRouteFinder.Finder;
 | 
			
		||||
using Ocelot.Infrastructure.RequestData;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
using Ocelot.Middleware;
 | 
			
		||||
using Ocelot.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.DownstreamRouteFinder.Middleware
 | 
			
		||||
{
 | 
			
		||||
@@ -29,7 +30,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
 | 
			
		||||
        {
 | 
			
		||||
            _logger.LogDebug("started calling downstream route finder middleware");
 | 
			
		||||
 | 
			
		||||
            var upstreamUrlPath = context.Request.Path.ToString();
 | 
			
		||||
            var upstreamUrlPath = context.Request.Path.ToString().SetLastCharacterAs('/');
 | 
			
		||||
 | 
			
		||||
            _logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
@@ -46,14 +47,16 @@ namespace Ocelot.Errors.Middleware
 | 
			
		||||
            _logger.LogDebug("ocelot pipeline finished");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static async Task SetInternalServerErrorOnResponse(HttpContext context)
 | 
			
		||||
        private async Task SetInternalServerErrorOnResponse(HttpContext context)
 | 
			
		||||
        {
 | 
			
		||||
            context.Response.StatusCode = 500;
 | 
			
		||||
            context.Response.ContentType = "application/json";
 | 
			
		||||
            await context.Response.WriteAsync("Internal Server Error");
 | 
			
		||||
            context.Response.OnStarting(x =>
 | 
			
		||||
            {
 | 
			
		||||
                context.Response.StatusCode = 500;
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            }, context);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static string CreateMessage(HttpContext context, Exception e)
 | 
			
		||||
        private string CreateMessage(HttpContext context, Exception e)
 | 
			
		||||
        {
 | 
			
		||||
            var message =
 | 
			
		||||
                $"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								src/Ocelot/Utilities/StringExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/Ocelot/Utilities/StringExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
namespace Ocelot.Utilities
 | 
			
		||||
{
 | 
			
		||||
    public static class StringExtensions
 | 
			
		||||
    {
 | 
			
		||||
        public static string SetLastCharacterAs(this string valueToSetLastChar, 
 | 
			
		||||
            char expectedLastChar)
 | 
			
		||||
        {
 | 
			
		||||
            var last = valueToSetLastChar[valueToSetLastChar.Length - 1];
 | 
			
		||||
 | 
			
		||||
            if (last != expectedLastChar)
 | 
			
		||||
            {
 | 
			
		||||
                valueToSetLastChar = $"{valueToSetLastChar}{expectedLastChar}";
 | 
			
		||||
            }
 | 
			
		||||
            return valueToSetLastChar;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -56,6 +56,80 @@ namespace Ocelot.AcceptanceTests
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_not_care_about_no_trailing()
 | 
			
		||||
        {
 | 
			
		||||
            var configuration = new FileConfiguration
 | 
			
		||||
            {
 | 
			
		||||
                ReRoutes = new List<FileReRoute>
 | 
			
		||||
                    {
 | 
			
		||||
                        new FileReRoute
 | 
			
		||||
                        {
 | 
			
		||||
                            DownstreamTemplate = "http://localhost:51879/products",
 | 
			
		||||
                            UpstreamTemplate = "/products/",
 | 
			
		||||
                            UpstreamHttpMethod = "Get",
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/products", 200, "Hello from Laura"))
 | 
			
		||||
                .And(x => _steps.GivenThereIsAConfiguration(configuration))
 | 
			
		||||
                .And(x => _steps.GivenOcelotIsRunning())
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products"))
 | 
			
		||||
                .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
 | 
			
		||||
                .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_not_care_about_trailing()
 | 
			
		||||
        {
 | 
			
		||||
            var configuration = new FileConfiguration
 | 
			
		||||
            {
 | 
			
		||||
                ReRoutes = new List<FileReRoute>
 | 
			
		||||
                    {
 | 
			
		||||
                        new FileReRoute
 | 
			
		||||
                        {
 | 
			
		||||
                            DownstreamTemplate = "http://localhost:51879/products",
 | 
			
		||||
                            UpstreamTemplate = "/products",
 | 
			
		||||
                            UpstreamHttpMethod = "Get",
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/products", 200, "Hello from Laura"))
 | 
			
		||||
                .And(x => _steps.GivenThereIsAConfiguration(configuration))
 | 
			
		||||
                .And(x => _steps.GivenOcelotIsRunning())
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
 | 
			
		||||
                .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
 | 
			
		||||
                .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_return_not_found()
 | 
			
		||||
        {
 | 
			
		||||
            var configuration = new FileConfiguration
 | 
			
		||||
            {
 | 
			
		||||
                ReRoutes = new List<FileReRoute>
 | 
			
		||||
                    {
 | 
			
		||||
                        new FileReRoute
 | 
			
		||||
                        {
 | 
			
		||||
                            DownstreamTemplate = "http://localhost:51879/products",
 | 
			
		||||
                            UpstreamTemplate = "/products/{productId}",
 | 
			
		||||
                            UpstreamHttpMethod = "Get",
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/products", 200, "Hello from Laura"))
 | 
			
		||||
                .And(x => _steps.GivenThereIsAConfiguration(configuration))
 | 
			
		||||
                .And(x => _steps.GivenOcelotIsRunning())
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
 | 
			
		||||
                .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_return_response_200_with_complex_url()
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -137,6 +137,12 @@
 | 
			
		||||
            "UpstreamTemplate": "/customers/{customerId}",
 | 
			
		||||
            "UpstreamHttpMethod": "Delete",
 | 
			
		||||
            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "DownstreamTemplate": "http://jsonplaceholder.typicode.com/posts",
 | 
			
		||||
            "UpstreamTemplate": "/posts/",
 | 
			
		||||
            "UpstreamHttpMethod": "Get",
 | 
			
		||||
            "FileCacheOptions": { "TtlSeconds": 15 }
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "GlobalConfiguration": {
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                        .WithDownstreamTemplate("/products/{productId}")
 | 
			
		||||
                        .WithUpstreamTemplate("/api/products/{productId}")
 | 
			
		||||
                        .WithUpstreamHttpMethod("Get")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("(?i)/api/products/.*$")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
 | 
			
		||||
                        .Build()
 | 
			
		||||
                }))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
@@ -88,7 +88,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                        .WithDownstreamTemplate("/products/{productId}")
 | 
			
		||||
                        .WithUpstreamTemplate("/api/products/{productId}")
 | 
			
		||||
                        .WithUpstreamHttpMethod("Get")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("(?i)/api/products/.*$")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("(?i)/api/products/.*/$")
 | 
			
		||||
                        .Build()
 | 
			
		||||
                }))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
@@ -118,7 +118,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                        .WithDownstreamTemplate("/products/{productId}")
 | 
			
		||||
                        .WithUpstreamTemplate("/api/products/{productId}")
 | 
			
		||||
                        .WithUpstreamHttpMethod("Get")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("/api/products/.*$")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
			
		||||
                        .Build()
 | 
			
		||||
              }))
 | 
			
		||||
              .BDDfy();
 | 
			
		||||
@@ -152,7 +152,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                        .WithDownstreamTemplate("/products/{productId}")
 | 
			
		||||
                        .WithUpstreamTemplate("/api/products/{productId}")
 | 
			
		||||
                        .WithUpstreamHttpMethod("Get")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("/api/products/.*$")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
			
		||||
                        .WithRequestIdKey("blahhhh")
 | 
			
		||||
                        .Build()
 | 
			
		||||
                }))
 | 
			
		||||
@@ -183,7 +183,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                        .WithDownstreamTemplate("/products/{productId}")
 | 
			
		||||
                        .WithUpstreamTemplate("/api/products/{productId}")
 | 
			
		||||
                        .WithUpstreamHttpMethod("Get")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("/api/products/.*$")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
			
		||||
                        .Build()
 | 
			
		||||
                }))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
@@ -198,7 +198,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                    .WithDownstreamTemplate("/products/{productId}")
 | 
			
		||||
                    .WithUpstreamTemplate("/api/products/{productId}")
 | 
			
		||||
                    .WithUpstreamHttpMethod("Get")
 | 
			
		||||
                    .WithUpstreamTemplatePattern("/api/products/.*$")
 | 
			
		||||
                    .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
			
		||||
                    .WithAuthenticationProvider("IdentityServer")
 | 
			
		||||
                    .WithAuthenticationProviderUrl("http://localhost:51888")
 | 
			
		||||
                    .WithRequireHttps(false)
 | 
			
		||||
@@ -261,7 +261,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                    .WithDownstreamTemplate("/products/{productId}")
 | 
			
		||||
                    .WithUpstreamTemplate("/api/products/{productId}")
 | 
			
		||||
                    .WithUpstreamHttpMethod("Get")
 | 
			
		||||
                    .WithUpstreamTemplatePattern("/api/products/.*$")
 | 
			
		||||
                    .WithUpstreamTemplatePattern("/api/products/.*/$")
 | 
			
		||||
                    .WithAuthenticationProvider("IdentityServer")
 | 
			
		||||
                    .WithAuthenticationProviderUrl("http://localhost:51888")
 | 
			
		||||
                    .WithRequireHttps(false)
 | 
			
		||||
@@ -323,7 +323,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                        .WithDownstreamTemplate("/products/{productId}")
 | 
			
		||||
                        .WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}")
 | 
			
		||||
                        .WithUpstreamHttpMethod("Get")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("/api/products/.*/variants/.*$")
 | 
			
		||||
                        .WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$")
 | 
			
		||||
                        .Build()
 | 
			
		||||
                }))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
 
 | 
			
		||||
@@ -148,7 +148,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
 | 
			
		||||
              .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        private void GivenIHaveAUpstreamPath(string downstreamPath)
 | 
			
		||||
        {
 | 
			
		||||
            _downstreamUrlPath = downstreamPath;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user