mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 18:00:48 +08:00 
			
		
		
		
	#238 support passing statefull service headers to service fabric naming service
This commit is contained in:
		@@ -33,3 +33,11 @@ The example below is taken from the samples folder so please check it if this do
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you are using stateless / guest exe services ocelot will be able to proxy through the naming service without anything else. However
 | 
				
			||||||
 | 
					if you are using statefull / actor services you must send the PartitionKind and PartitionKey query string values with the client
 | 
				
			||||||
 | 
					request e.g.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GET http://ocelot.com/EquipmentInterfaces?PartitionKind=xxx&PartitionKey=xxx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There is no way for Ocelot to work these out for you. 
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ author: raunakpandya edited by Tom Pallister for Ocelot
 | 
				
			|||||||
This shows a service fabric cluster with Ocelot exposed over HTTP accessing services in the cluster via the naming service. If you want to try and use Ocelot with
 | 
					This shows a service fabric cluster with Ocelot exposed over HTTP accessing services in the cluster via the naming service. If you want to try and use Ocelot with
 | 
				
			||||||
Service Fabric I reccomend using this as a starting point.
 | 
					Service Fabric I reccomend using this as a starting point.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ocelot does not support partitioned services (stateful & actors) at the moment. 
 | 
					If you want to use statefull / actors you must send the PartitionKind and PartitionKey to Ocelot as query string parameters.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
I have not tested this sample on Service Fabric hosted on Linux just a Windows dev cluster. This sample assumes a good understanding of Service Fabric.
 | 
					I have not tested this sample on Service Fabric hosted on Linux just a Windows dev cluster. This sample assumes a good understanding of Service Fabric.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,8 @@ using Ocelot.Middleware;
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using Ocelot.DownstreamRouteFinder.Middleware;
 | 
					using Ocelot.DownstreamRouteFinder.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
					namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -40,21 +42,9 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            UriBuilder uriBuilder;
 | 
					            UriBuilder uriBuilder;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            //todo - feel this is a bit crap the way we build the url dont see why we need this builder thing..maybe i blew my own brains out 
 | 
					            if (ServiceFabricRequest(context))
 | 
				
			||||||
            // when i originally wrote it..
 | 
					 | 
				
			||||||
            if (context.ServiceProviderConfiguration.Type == "ServiceFabric" && context.DownstreamReRoute.UseServiceDiscovery)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _logger.LogInformation("DownstreamUrlCreatorMiddleware - going to try set service fabric path");
 | 
					                uriBuilder = CreateServiceFabricUri(context, dsPath);
 | 
				
			||||||
 | 
					 | 
				
			||||||
                var scheme = context.DownstreamReRoute.DownstreamScheme;
 | 
					 | 
				
			||||||
                var host = context.DownstreamRequest.RequestUri.Host;
 | 
					 | 
				
			||||||
                var port = context.DownstreamRequest.RequestUri.Port;
 | 
					 | 
				
			||||||
                var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _logger.LogInformation("DownstreamUrlCreatorMiddleware - service fabric path is {proxyUrl}", serviceFabricPath);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}?cmd=instance");
 | 
					 | 
				
			||||||
                uriBuilder = new UriBuilder(uri);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -71,5 +61,38 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            await _next.Invoke(context);
 | 
					            await _next.Invoke(context);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private UriBuilder CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var query = context.DownstreamRequest.RequestUri.Query;
 | 
				
			||||||
 | 
					            var scheme = context.DownstreamReRoute.DownstreamScheme;
 | 
				
			||||||
 | 
					            var host = context.DownstreamRequest.RequestUri.Host;
 | 
				
			||||||
 | 
					            var port = context.DownstreamRequest.RequestUri.Port;
 | 
				
			||||||
 | 
					            var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Uri uri;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (RequestForStatefullService(query))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}{query}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var split = string.IsNullOrEmpty(query) ? "?" : "&";
 | 
				
			||||||
 | 
					                uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}{query}{split}cmd=instance");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new UriBuilder(uri);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static bool ServiceFabricRequest(DownstreamContext context)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return context.ServiceProviderConfiguration.Type == "ServiceFabric" && context.DownstreamReRoute.UseServiceDiscovery;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static bool RequestForStatefullService(string query)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return query.Contains("PartitionKind") && query.Contains("PartitionKey");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_support_service_fabric_naming_and_dns_service()
 | 
					        public void should_support_service_fabric_naming_and_dns_service_stateless_and_guest()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var configuration = new FileConfiguration
 | 
					            var configuration = new FileConfiguration
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -54,7 +54,7 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:19081", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura"))
 | 
					            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:19081", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "cmd=instance"))
 | 
				
			||||||
                .And(x => _steps.GivenThereIsAConfiguration(configuration))
 | 
					                .And(x => _steps.GivenThereIsAConfiguration(configuration))
 | 
				
			||||||
                .And(x => _steps.GivenOcelotIsRunning())
 | 
					                .And(x => _steps.GivenOcelotIsRunning())
 | 
				
			||||||
                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces"))
 | 
					                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces"))
 | 
				
			||||||
@@ -63,7 +63,44 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_support_service_fabric_naming_and_dns_service_statefull_and_actors()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var configuration = new FileConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    new FileReRoute
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        DownstreamPathTemplate = "/api/values",
 | 
				
			||||||
 | 
					                        DownstreamScheme = "http",
 | 
				
			||||||
 | 
					                        UpstreamPathTemplate = "/EquipmentInterfaces",
 | 
				
			||||||
 | 
					                        UpstreamHttpMethod = new List<string> { "Get" },
 | 
				
			||||||
 | 
					                        UseServiceDiscovery = true,
 | 
				
			||||||
 | 
					                        ServiceName = "OcelotServiceApplication/OcelotApplicationService"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                GlobalConfiguration = new FileGlobalConfiguration
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Host = "localhost",
 | 
				
			||||||
 | 
					                        Port = 19081,
 | 
				
			||||||
 | 
					                        Type = "ServiceFabric"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:19081", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "PartitionKind=test&PartitionKey=1"))
 | 
				
			||||||
 | 
					                .And(x => _steps.GivenThereIsAConfiguration(configuration))
 | 
				
			||||||
 | 
					                .And(x => _steps.GivenOcelotIsRunning())
 | 
				
			||||||
 | 
					                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces?PartitionKind=test&PartitionKey=1"))
 | 
				
			||||||
 | 
					                .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
 | 
				
			||||||
 | 
					                .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody, string expectedQueryString)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _builder = new WebHostBuilder()
 | 
					            _builder = new WebHostBuilder()
 | 
				
			||||||
                .UseUrls(baseUrl)
 | 
					                .UseUrls(baseUrl)
 | 
				
			||||||
@@ -84,9 +121,8 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        else
 | 
					                        else
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            if (context.Request.Query.TryGetValue("cmd", out var values))
 | 
					                            if (context.Request.QueryString.Value.Contains(expectedQueryString))
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                values.First().ShouldBe("instance");
 | 
					 | 
				
			||||||
                                context.Response.StatusCode = statusCode;
 | 
					                                context.Response.StatusCode = statusCode;
 | 
				
			||||||
                                await context.Response.WriteAsync(responseBody);
 | 
					                                await context.Response.WriteAsync(responseBody);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,6 +135,66 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
 | 
				
			|||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_service_fabric_url_with_query_string_for_stateless_service()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var downstreamReRoute = new DownstreamReRouteBuilder()
 | 
				
			||||||
 | 
					                .WithDownstreamScheme("http")
 | 
				
			||||||
 | 
					                .WithServiceName("Ocelot/OcelotApp")
 | 
				
			||||||
 | 
					                .WithUseServiceDiscovery(true)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var downstreamRoute = new DownstreamRoute(
 | 
				
			||||||
 | 
					                new List<PlaceholderNameAndValue>(),
 | 
				
			||||||
 | 
					                new ReRouteBuilder()
 | 
				
			||||||
 | 
					                    .WithDownstreamReRoute(downstreamReRoute)
 | 
				
			||||||
 | 
					                    .Build());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var config = new ServiceProviderConfigurationBuilder()
 | 
				
			||||||
 | 
					                .WithServiceDiscoveryProviderType("ServiceFabric")
 | 
				
			||||||
 | 
					                .WithServiceDiscoveryProviderHost("localhost")
 | 
				
			||||||
 | 
					                .WithServiceDiscoveryProviderPort(19081)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
 | 
				
			||||||
 | 
					                .And(x => GivenTheServiceProviderConfigIs(config))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081?Tom=test&laura=1"))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICallTheMiddleware())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?Tom=test&laura=1&cmd=instance"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_create_service_fabric_url_with_query_string_for_stateful_service()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var downstreamReRoute = new DownstreamReRouteBuilder()
 | 
				
			||||||
 | 
					                .WithDownstreamScheme("http")
 | 
				
			||||||
 | 
					                .WithServiceName("Ocelot/OcelotApp")
 | 
				
			||||||
 | 
					                .WithUseServiceDiscovery(true)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var downstreamRoute = new DownstreamRoute(
 | 
				
			||||||
 | 
					                new List<PlaceholderNameAndValue>(),
 | 
				
			||||||
 | 
					                new ReRouteBuilder()
 | 
				
			||||||
 | 
					                    .WithDownstreamReRoute(downstreamReRoute)
 | 
				
			||||||
 | 
					                    .Build());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var config = new ServiceProviderConfigurationBuilder()
 | 
				
			||||||
 | 
					                .WithServiceDiscoveryProviderType("ServiceFabric")
 | 
				
			||||||
 | 
					                .WithServiceDiscoveryProviderHost("localhost")
 | 
				
			||||||
 | 
					                .WithServiceDiscoveryProviderPort(19081)
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
 | 
				
			||||||
 | 
					                .And(x => GivenTheServiceProviderConfigIs(config))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081?PartitionKind=test&PartitionKey=1"))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICallTheMiddleware())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?PartitionKind=test&PartitionKey=1"))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
 | 
					        private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamContext.ServiceProviderConfiguration = config;
 | 
					            _downstreamContext.ServiceProviderConfiguration = config;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user