diff --git a/gateways/web-public/src/EShopOnAbp.WebPublicGateway/EShopOnAbpWebPublicGatewayModule.cs b/gateways/web-public/src/EShopOnAbp.WebPublicGateway/EShopOnAbpWebPublicGatewayModule.cs index a77e5660..6609c638 100644 --- a/gateways/web-public/src/EShopOnAbp.WebPublicGateway/EShopOnAbpWebPublicGatewayModule.cs +++ b/gateways/web-public/src/EShopOnAbp.WebPublicGateway/EShopOnAbpWebPublicGatewayModule.cs @@ -1,12 +1,10 @@ using EShopOnAbp.Shared.Hosting.Gateways; using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Ocelot.Middleware; using System.Collections.Generic; -using System.Linq; using EShopOnAbp.Shared.Hosting.AspNetCore; +using Microsoft.AspNetCore.Http; using Volo.Abp; using Volo.Abp.Modularity; using Microsoft.AspNetCore.Rewrite; @@ -38,6 +36,9 @@ public class EShopOnAbpWebPublicGatewayModule : AbpModule }, apiTitle: "WebPublic Gateway" ); + + // context.Services.AddReverseProxy() + // .LoadFromConfig(configuration.GetSection("ReverseProxy")); } public override void OnApplicationInitialization(ApplicationInitializationContext context) @@ -52,35 +53,17 @@ public class EShopOnAbpWebPublicGatewayModule : AbpModule app.UseCorrelationId(); app.UseAbpSerilogEnrichers(); - app.UseSwagger(); - app.UseSwaggerUI(options => - { - var configuration = context.ServiceProvider.GetRequiredService(); - var routes = configuration.GetSection("Routes").Get>(); - var routedServices = routes - .GroupBy(t => t.ServiceKey) - .Select(r => r.First()) - .Distinct(); - - foreach (var config in routedServices) - { - var url = - $"{config.DownstreamScheme}://{config.DownstreamHostAndPorts.FirstOrDefault()?.Host}:{config.DownstreamHostAndPorts.FirstOrDefault()?.Port}"; - if (!env.IsDevelopment()) - { - url = $"https://{config.DownstreamHostAndPorts.FirstOrDefault()?.Host}"; - } - - options.SwaggerEndpoint($"{url}/swagger/v1/swagger.json", $"{config.ServiceKey} API"); - options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]); - options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]); - } - }); - + app.ConfigureSwaggerUIWithYarp(context); + app.UseRewriter(new RewriteOptions() // Regex for "", "/" and "" (whitespace) .AddRedirect("^(|\\|\\s+)$", "/swagger")); - - app.UseOcelot().Wait(); + + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGet("", ctx => ctx.Response.WriteAsync("YAG")); + endpoints.MapReverseProxy(); + }); } } \ No newline at end of file diff --git a/gateways/web-public/src/EShopOnAbp.WebPublicGateway/appsettings.json b/gateways/web-public/src/EShopOnAbp.WebPublicGateway/appsettings.json index dfe10330..f1417cac 100644 --- a/gateways/web-public/src/EShopOnAbp.WebPublicGateway/appsettings.json +++ b/gateways/web-public/src/EShopOnAbp.WebPublicGateway/appsettings.json @@ -17,7 +17,6 @@ } }, "AllowedHosts": "*", - "Redis": { "Configuration": "localhost:6379" }, diff --git a/gateways/web-public/src/EShopOnAbp.WebPublicGateway/ocelot.json b/gateways/web-public/src/EShopOnAbp.WebPublicGateway/ocelot.json deleted file mode 100644 index a436b432..00000000 --- a/gateways/web-public/src/EShopOnAbp.WebPublicGateway/ocelot.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "GlobalConfiguration": { - "BaseUrl": "https://localhost:44373" - }, - "Routes": [ - { - "ServiceKey": "Account Service", - "DownstreamPathTemplate": "/api/account/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44330 - } - ], - "UpstreamPathTemplate": "/api/account/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ] - }, - { - "ServiceKey": "Administration Service", - "DownstreamPathTemplate": "/api/abp/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44353 - } - ], - "UpstreamPathTemplate": "/api/abp/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 3000, - "TimeoutValue": 2500 - } - }, - { - "ServiceKey": "Catalog Service", - "DownstreamPathTemplate": "/api/catalog/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44354 - } - ], - "UpstreamPathTemplate": "/api/catalog/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 3000, - "TimeoutValue": 2500 - } - }, - { - "ServiceKey": "Catalog Service", - "DownstreamPathTemplate": "/product-images/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44354 - } - ], - "UpstreamPathTemplate": "/product-images/{everything}", - "UpstreamHttpMethod": [ "Get" ] - }, - { - "ServiceKey": "Basket Service", - "DownstreamPathTemplate": "/api/basket/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44355 - } - ], - "UpstreamPathTemplate": "/api/basket/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 3000, - "TimeoutValue": 2500 - } - }, - { - "ServiceKey": "Ordering Service", - "DownstreamPathTemplate": "/api/ordering/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44356 - } - ], - "UpstreamPathTemplate": "/api/ordering/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ] - }, - { - "ServiceKey": "Payment Service", - "DownstreamPathTemplate": "/api/payment/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44357 - } - ], - "UpstreamPathTemplate": "/api/payment/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ] - } - ] -} \ No newline at end of file diff --git a/gateways/web-public/src/EShopOnAbp.WebPublicGateway/yarp.json b/gateways/web-public/src/EShopOnAbp.WebPublicGateway/yarp.json new file mode 100644 index 00000000..7e3b617a --- /dev/null +++ b/gateways/web-public/src/EShopOnAbp.WebPublicGateway/yarp.json @@ -0,0 +1,100 @@ +{ + "ReverseProxy": { + "Routes": { + "Account Service": { + "ClusterId": "account-cluster", + "Match": { + "Path": "/api/account/{**everything}" + } + }, + "Administration Service": { + "ClusterId": "administration-cluster", + "Match": { + "Path": "/api/abp/{**everything}" + } + }, + "Catalog Service": { + "ClusterId": "catalog-cluster", + "Match": { + "Path": "/api/catalog/{**everything}" + } + }, + "Basket Service": { + "ClusterId": "basket-cluster", + "Match": { + "Path": "/api/basket/{**everything}" + } + }, + "Ordering Service": { + "ClusterId": "ordering-cluster", + "Match": { + "Path": "/api/ordering/{**everything}" + } + }, + "Payment Service": { + "ClusterId": "payment-cluster", + "Match": { + "Path": "/api/payment/{**everything}" + } + }, + "product-picture-route": { + "ClusterId": "product-picture-cluster", + "Match": { + "Path": "/product-images/{**everything}", + "Methods" : [ "GET" ] + } + } + }, + "Clusters": { + "account-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44330" + } + } + }, + "administration-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44353" + } + } + }, + "catalog-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44354" + } + } + }, + "product-picture-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44354" + } + } + }, + "basket-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44355" + } + } + }, + "ordering-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44356" + } + } + }, + "payment-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44357" + } + } + } + } + } +} \ No newline at end of file diff --git a/gateways/web/src/EShopOnAbp.WebGateway/EShopOnAbpWebGatewayModule.cs b/gateways/web/src/EShopOnAbp.WebGateway/EShopOnAbpWebGatewayModule.cs index 232fafb2..ae4cb249 100644 --- a/gateways/web/src/EShopOnAbp.WebGateway/EShopOnAbpWebGatewayModule.cs +++ b/gateways/web/src/EShopOnAbp.WebGateway/EShopOnAbpWebGatewayModule.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Cors; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Ocelot.Middleware; using System; using System.Collections.Generic; using System.Linq; @@ -71,39 +70,15 @@ public class EShopOnAbpWebGatewayModule : AbpModule { app.UseDeveloperExceptionPage(); } - app.UseCorrelationId(); app.UseAbpSerilogEnrichers(); app.UseCors(); - app.UseSwagger(); - app.UseSwaggerUI(options => - { - var configuration = context.ServiceProvider.GetRequiredService(); - var routes = configuration.GetSection("Routes").Get>(); - var routedServices = routes - .GroupBy(t => t.ServiceKey) - .Select(r => r.First()) - .Distinct(); - - foreach (var config in routedServices) - { - var url = - $"{config.DownstreamScheme}://{config.DownstreamHostAndPorts.FirstOrDefault()?.Host}:{config.DownstreamHostAndPorts.FirstOrDefault()?.Port}"; - if (!env.IsDevelopment()) - { - url = $"https://{config.DownstreamHostAndPorts.FirstOrDefault()?.Host}"; - } - - options.SwaggerEndpoint($"{url}/swagger/v1/swagger.json", $"{config.ServiceKey} API"); - options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]); - options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]); - } - }); + app.ConfigureSwaggerUIWithYarp(context); app.UseRewriter(new RewriteOptions() // Regex for "", "/" and "" (whitespace) .AddRedirect("^(|\\|\\s+)$", "/swagger")); - app.UseOcelot().Wait(); + // app.UseOcelot().Wait(); } } \ No newline at end of file diff --git a/gateways/web/src/EShopOnAbp.WebGateway/ocelot.json b/gateways/web/src/EShopOnAbp.WebGateway/ocelot.json deleted file mode 100644 index affe35be..00000000 --- a/gateways/web/src/EShopOnAbp.WebGateway/ocelot.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "GlobalConfiguration": { - "BaseUrl": "https://localhost:44372" - }, - "Routes": [ - { - "ServiceKey": "Identity Service", - "DownstreamPathTemplate": "/api/identity/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44351 - } - ], - "UpstreamPathTemplate": "/api/identity/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ] - }, - { - "ServiceKey": "Account Service", - "DownstreamPathTemplate": "/api/account/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44330 - } - ], - "UpstreamPathTemplate": "/api/account/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ] - }, - { - "ServiceKey": "Administration Service", - "DownstreamPathTemplate": "/api/abp/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44353 - } - ], - "UpstreamPathTemplate": "/api/abp/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 5000, - "TimeoutValue": 2500 - } - }, - { - "ServiceKey": "Administration Service", - "DownstreamPathTemplate": "/api/feature-management/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44353 - } - ], - "UpstreamPathTemplate": "/api/feature-management/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ] - }, - { - "ServiceKey": "Administration Service", - "DownstreamPathTemplate": "/api/permission-management/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44353 - } - ], - "UpstreamPathTemplate": "/api/permission-management/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ] - }, - { - "ServiceKey": "Administration Service", - "DownstreamPathTemplate": "/api/setting-management/{everything}", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 44353 - } - ], - "UpstreamPathTemplate": "/api/setting-management/{everything}", - "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ] - } - ] -} \ No newline at end of file diff --git a/gateways/web/src/EShopOnAbp.WebGateway/yarp.json b/gateways/web/src/EShopOnAbp.WebGateway/yarp.json new file mode 100644 index 00000000..424e598e --- /dev/null +++ b/gateways/web/src/EShopOnAbp.WebGateway/yarp.json @@ -0,0 +1,86 @@ +{ + "ReverseProxy": { + "Routes": { + "Account Service": { + "ClusterId": "account-cluster", + "Match": { + "Path": "/api/account/{**everything}" + } + }, + "Identity Service": { + "ClusterId": "identity-cluster", + "Match": { + "Path": "/api/identity/{**everything}" + } + }, + "Administration Service": { + "ClusterId": "administration-cluster", + "Match": { + "Path": "/api/abp/{**everything}" + } + }, + "feature-management-route": { + "ClusterId": "feature-management-cluster", + "Match": { + "Path": "/api/feature-management/{**everything}" + } + }, + "permission-management-route": { + "ClusterId": "permission-management-cluster", + "Match": { + "Path": "/api/permission-management/{**everything}" + } + }, + "setting-management-route": { + "ClusterId": "setting-management-cluster", + "Match": { + "Path": "/api/setting-management/{**everything}" + } + } + }, + "Clusters": { + "account-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44330" + } + } + }, + "identity-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44351" + } + } + }, + "administration-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44353" + } + } + }, + "feature-management-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44353" + } + } + }, + "permission-management-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44353" + } + } + }, + "setting-management-cluster": { + "Destinations": { + "destination1": { + "Address": "https://localhost:44353" + } + } + } + } + } +} \ No newline at end of file diff --git a/shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbp.Shared.Hosting.Gateways.csproj b/shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbp.Shared.Hosting.Gateways.csproj index 33dfe14e..c6e72cb3 100644 --- a/shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbp.Shared.Hosting.Gateways.csproj +++ b/shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbp.Shared.Hosting.Gateways.csproj @@ -5,12 +5,7 @@ - - - - - - + diff --git a/shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbpSharedHostingGatewaysModule.cs b/shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbpSharedHostingGatewaysModule.cs index 895cfabf..11c1d509 100644 --- a/shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbpSharedHostingGatewaysModule.cs +++ b/shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbpSharedHostingGatewaysModule.cs @@ -1,24 +1,20 @@ using EShopOnAbp.Shared.Hosting.AspNetCore; using Microsoft.Extensions.DependencyInjection; -using Ocelot.DependencyInjection; -using Ocelot.Provider.Polly; -using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy; using Volo.Abp.Modularity; namespace EShopOnAbp.Shared.Hosting.Gateways { [DependsOn( - typeof(EShopOnAbpSharedHostingAspNetCoreModule), - typeof(AbpAspNetCoreMvcUiMultiTenancyModule) + typeof(EShopOnAbpSharedHostingAspNetCoreModule) )] public class EShopOnAbpSharedHostingGatewaysModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); - - context.Services.AddOcelot(configuration) - .AddPolly(); + + context.Services.AddReverseProxy() + .LoadFromConfig(configuration.GetSection("ReverseProxy")); } } } \ No newline at end of file diff --git a/shared/EShopOnAbp.Shared.Hosting.Gateways/GatewayHostBuilderExtensions.cs b/shared/EShopOnAbp.Shared.Hosting.Gateways/GatewayHostBuilderExtensions.cs index b53c608f..a2663f9a 100644 --- a/shared/EShopOnAbp.Shared.Hosting.Gateways/GatewayHostBuilderExtensions.cs +++ b/shared/EShopOnAbp.Shared.Hosting.Gateways/GatewayHostBuilderExtensions.cs @@ -4,18 +4,18 @@ namespace Microsoft.Extensions.Hosting; public static class AbpHostingHostBuilderExtensions { - public const string AppOcelotJsonPath = "ocelot.json"; + public const string AppYarpJsonPath = "yarp.json"; public static IHostBuilder AddOcelotJson( this IHostBuilder hostBuilder, bool optional = true, bool reloadOnChange = true, - string path = AppOcelotJsonPath) + string path = AppYarpJsonPath) { return hostBuilder.ConfigureAppConfiguration((_, builder) => { builder.AddJsonFile( - path: AppOcelotJsonPath, + path: AppYarpJsonPath, optional: optional, reloadOnChange: reloadOnChange ); diff --git a/shared/EShopOnAbp.Shared.Hosting.Gateways/OcelotConfiguration.cs b/shared/EShopOnAbp.Shared.Hosting.Gateways/OcelotConfiguration.cs deleted file mode 100644 index 288b697e..00000000 --- a/shared/EShopOnAbp.Shared.Hosting.Gateways/OcelotConfiguration.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; - -namespace EShopOnAbp.Shared.Hosting.Gateways -{ - public class OcelotConfiguration - { - public string ServiceKey { get; set; } - public string DownstreamPathTemplate { get; set; } - public string DownstreamScheme { get; set; } - public string UpstreamPathTemplate { get; set; } - public List UpstreamHttpMethod { get; set; } - public List DownstreamHostAndPorts { get; set; } - } - - public class HostAndPort - { - public string Host { get; set; } - public int Port { get; set; } - } -} \ No newline at end of file diff --git a/shared/EShopOnAbp.Shared.Hosting.Gateways/YarpSwaggerUIBuilderExtensions.cs b/shared/EShopOnAbp.Shared.Hosting.Gateways/YarpSwaggerUIBuilderExtensions.cs new file mode 100644 index 00000000..7a8cb5e0 --- /dev/null +++ b/shared/EShopOnAbp.Shared.Hosting.Gateways/YarpSwaggerUIBuilderExtensions.cs @@ -0,0 +1,52 @@ +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Volo.Abp; +using Yarp.ReverseProxy.Configuration; + +namespace EShopOnAbp.Shared.Hosting.Gateways; + +public static class YarpSwaggerUIBuilderExtensions +{ + public static IApplicationBuilder ConfigureSwaggerUIWithYarp(this IApplicationBuilder app, + ApplicationInitializationContext context) + { + app.UseSwagger(); + app.UseSwaggerUI(options => + { + var configuration = context.ServiceProvider.GetRequiredService(); + var logger = context.ServiceProvider.GetRequiredService>(); + var proxyConfigProvider = context.ServiceProvider.GetRequiredService(); + var yarpConfig = proxyConfigProvider.GetConfig(); + + var routedClusters = yarpConfig.Clusters + .SelectMany(t => t.Destinations, + (clusterId, destination) => new {clusterId.ClusterId, destination.Value}); + + var groupedClusters = routedClusters + .GroupBy(q => q.Value.Address) + .Select(t => t.First()) + .Distinct() + .ToList(); + + foreach (var clusterGroup in groupedClusters) + { + var routeConfig = yarpConfig.Routes.FirstOrDefault(q => + q.ClusterId == clusterGroup.ClusterId); + if (routeConfig == null) + { + logger.LogWarning($"Swagger UI: Couldn't find route configuration for {clusterGroup.ClusterId}..."); + continue; + } + + options.SwaggerEndpoint($"{clusterGroup.Value.Address}/swagger/v1/swagger.json", $"{routeConfig.RouteId} API"); + options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]); + options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]); + } + }); + + return app; + } +} \ No newline at end of file