feat 网关继承微服务Swagger

This commit is contained in:
2025-11-01 23:30:58 +08:00
parent ccb12389ee
commit 3cc7d2b85d
30 changed files with 1385 additions and 68 deletions

View File

@ -0,0 +1,91 @@
using KonSoft.InternalGateway.Aggregations.Base;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
namespace KonSoft.InternalGateway.Aggregations.ApplicationConfiguration;
public class AppConfigurationAggregation : AggregateServiceBase<ApplicationConfigurationDto>,
IAppConfigurationAggregation, ITransientDependency
{
public string AppConfigRouteName => "EshopOnAbpApplicationConfiguration";
public string AppConfigEndpoint => "api/abp/application-configuration";
protected IAppConfigurationRemoteService AppConfigurationRemoteService { get; }
public AppConfigurationAggregation(
IAppConfigurationRemoteService appConfigurationRemoteService) : base(
appConfigurationRemoteService)
{
AppConfigurationRemoteService = appConfigurationRemoteService;
}
public async Task<ApplicationConfigurationDto> GetAppConfigurationAsync(AppConfigurationRequest input)
{
var remoteAppConfigurationResults =
await AppConfigurationRemoteService.GetMultipleAsync(input.Endpoints);
//merge only application configuration settings data
var mergedResult = MergeAppConfigurationSettingsData(remoteAppConfigurationResults);
//return result
return mergedResult;
}
private static ApplicationConfigurationDto MergeAppConfigurationSettingsData(
IDictionary<string, ApplicationConfigurationDto> appConfigurations)
{
var appConfigurationDto = CreateInitialAppConfigDto(appConfigurations);
foreach (var (_, appConfig) in appConfigurations)
{
foreach (var resource in appConfig.Setting.Values)
{
appConfigurationDto.Setting.Values.TryAdd(resource.Key, resource.Value);
}
}
return appConfigurationDto;
}
/// <summary>
/// Checks "Administration" clusterId to set the initial data from the AdministrationService.
/// Otherwise uses the first available service for the initial application configuration data
/// </summary>
/// <param name="appConfigurations"></param>
/// <returns></returns>
private static ApplicationConfigurationDto CreateInitialAppConfigDto(
IDictionary<string, ApplicationConfigurationDto> appConfigurations
)
{
if (appConfigurations.Count == 0)
{
return new ApplicationConfigurationDto();
}
if (appConfigurations.TryGetValue("Administration_AppConfig", out var administrationServiceData))
{
return MapServiceData(administrationServiceData);
}
return MapServiceData(appConfigurations.First().Value);
}
private static ApplicationConfigurationDto MapServiceData(ApplicationConfigurationDto appConfiguration)
{
return new ApplicationConfigurationDto
{
Localization = appConfiguration.Localization,
Auth = appConfiguration.Auth,
Clock = appConfiguration.Clock,
Setting = appConfiguration.Setting,
Features = appConfiguration.Features,
Timing = appConfiguration.Timing,
CurrentTenant = appConfiguration.CurrentTenant,
CurrentUser = appConfiguration.CurrentUser,
ExtraProperties = appConfiguration.ExtraProperties,
GlobalFeatures = appConfiguration.GlobalFeatures,
MultiTenancy = appConfiguration.MultiTenancy,
ObjectExtensions = appConfiguration.ObjectExtensions
};
}
}

View File

@ -0,0 +1,9 @@
using KonSoft.InternalGateway.Aggregations.Base;
using Microsoft.Extensions.Caching.Memory;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
namespace KonSoft.InternalGateway.Aggregations.ApplicationConfiguration;
public class AppConfigurationCachedService(IMemoryCache applicationConfigurationCache)
: CachedServiceBase<ApplicationConfigurationDto>(applicationConfigurationCache), ISingletonDependency;

View File

@ -0,0 +1,13 @@
using KonSoft.InternalGateway.Aggregations.Base;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace KonSoft.InternalGateway.Aggregations.ApplicationConfiguration;
public class AppConfigurationRemoteService(
IHttpContextAccessor httpContextAccessor,
IJsonSerializer jsonSerializer,
ILogger<AggregateRemoteServiceBase<ApplicationConfigurationDto>> logger)
: AggregateRemoteServiceBase<ApplicationConfigurationDto>(httpContextAccessor, jsonSerializer, logger),
IAppConfigurationRemoteService, ITransientDependency;

View File

@ -0,0 +1,8 @@
using KonSoft.InternalGateway.Aggregations.Base;
namespace KonSoft.InternalGateway.Aggregations.ApplicationConfiguration;
public class AppConfigurationRequest : IRequestInput
{
public Dictionary<string, string> Endpoints { get; } = new();
}

View File

@ -0,0 +1,10 @@
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
namespace KonSoft.InternalGateway.Aggregations.ApplicationConfiguration;
public interface IAppConfigurationAggregation
{
string AppConfigRouteName { get; }
string AppConfigEndpoint { get; }
Task<ApplicationConfigurationDto> GetAppConfigurationAsync(AppConfigurationRequest input);
}

View File

@ -0,0 +1,6 @@
using KonSoft.InternalGateway.Aggregations.Base;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
namespace KonSoft.InternalGateway.Aggregations.ApplicationConfiguration;
public interface IAppConfigurationRemoteService : IAggregateRemoteService<ApplicationConfigurationDto>;

View File

@ -0,0 +1,103 @@
using Volo.Abp.Json;
namespace KonSoft.InternalGateway.Aggregations.Base;
public abstract class AggregateRemoteServiceBase<TDto> : IAggregateRemoteService<TDto>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<AggregateRemoteServiceBase<TDto>> _logger;
protected IJsonSerializer JsonSerializer { get; }
protected AggregateRemoteServiceBase(IHttpContextAccessor httpContextAccessor, IJsonSerializer jsonSerializer,
ILogger<AggregateRemoteServiceBase<TDto>> logger)
{
_httpContextAccessor = httpContextAccessor;
JsonSerializer = jsonSerializer;
_logger = logger;
}
public async Task<Dictionary<string, TDto>> GetMultipleAsync(
Dictionary<string, string> serviceNameWithUrlDictionary)
{
Dictionary<string, Task<TDto>> completedTasks = new Dictionary<string, Task<TDto>>();
Dictionary<string, Task<TDto>> runningTasks = new Dictionary<string, Task<TDto>>();
Dictionary<string, TDto> completedResult = new Dictionary<string, TDto>();
using (HttpClient httpClient = CreateHttpClient())
{
foreach (var service in serviceNameWithUrlDictionary)
{
Task<TDto> requestTask =
MakeRequestAsync<TDto>(httpClient, service.Value);
runningTasks.Add(service.Key, requestTask);
}
while (runningTasks.Count > 0)
{
KeyValuePair<string, Task<TDto>> completedTask = await WaitForAnyTaskAsync(runningTasks);
runningTasks.Remove(completedTask.Key);
try
{
TDto result = await completedTask.Value;
completedTasks.Add(completedTask.Key, completedTask.Value);
completedResult.Add(completedTask.Key, result);
_logger.LogInformation("Localization Key: {0}, Value: {1}", completedTask.Key, result);
}
catch (Exception ex)
{
_logger.LogInformation("Error for the {0}: {1}", completedTask.Key, ex.Message);
}
}
}
return completedResult;
}
private HttpClient CreateHttpClient()
{
var httpClient = new HttpClient();
var headers = _httpContextAccessor.HttpContext?.Request.Headers;
if (headers != null)
{
foreach (var header in headers)
{
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value.ToArray());
}
}
return httpClient;
}
public async Task<T> MakeRequestAsync<T>(HttpClient httpClient, string url)
{
try
{
HttpResponseMessage response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<T>(content);
}
catch (Exception e)
{
_logger.LogInformation("Error making request to {0}: {1}", url, e.Message);
throw;
}
}
public async Task<KeyValuePair<TKey, Task<TValue>>> WaitForAnyTaskAsync<TKey, TValue>(
Dictionary<TKey, Task<TValue>> tasks)
{
var completedTask = Task.WhenAny(tasks.Values);
var result = await completedTask;
var completedTaskPair = tasks.First(kv => kv.Value == result);
return completedTaskPair;
}
}

View File

@ -0,0 +1,30 @@
namespace KonSoft.InternalGateway.Aggregations.Base;
public abstract class AggregateServiceBase<TDto>
{
private readonly IAggregateRemoteService<TDto> _remoteService;
public AggregateServiceBase(IAggregateRemoteService<TDto> remoteService)
{
_remoteService = remoteService;
}
public virtual async Task<Dictionary<string, TDto>> GetMultipleFromRemoteAsync(List<string> missingKeys,
Dictionary<string, string> endpoints)
{
return await _remoteService
.GetMultipleAsync(endpoints
.Where(kv => missingKeys.Contains(kv.Key))
.ToDictionary(k => k.Key, v => v.Value));
}
public List<string> GetMissingServiceKeys(
IDictionary<string, TDto> serviceNamesWithData,
Dictionary<string, string> serviceNamesWithUrls)
{
List<string> missingKeysInCache = serviceNamesWithUrls.Keys.Except(serviceNamesWithData.Keys).ToList();
List<string> missingKeysInUrls = serviceNamesWithData.Keys.Except(serviceNamesWithUrls.Keys).ToList();
return missingKeysInCache.Concat(missingKeysInUrls).ToList();
}
}

View File

@ -0,0 +1,39 @@
using Microsoft.Extensions.Caching.Memory;
namespace KonSoft.InternalGateway.Aggregations.Base;
public abstract class CachedServiceBase<TCacheValue> : ICachedServiceBase<TCacheValue>
{
private readonly IMemoryCache _cache;
protected MemoryCacheEntryOptions CacheEntryOptions { get; } = new()
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24),
SlidingExpiration = TimeSpan.FromHours(4)
};
protected CachedServiceBase(IMemoryCache cache)
{
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
}
public void Add(string serviceName, TCacheValue data)
{
_cache.Set(serviceName, data, CacheEntryOptions);
}
public IDictionary<string, TCacheValue> GetManyAsync(IEnumerable<string> serviceNames)
{
var result = new Dictionary<string, TCacheValue>();
foreach (var serviceName in serviceNames)
{
if (_cache.TryGetValue(serviceName, out TCacheValue data))
{
result.Add(serviceName, data);
}
}
return result;
}
}

View File

@ -0,0 +1,8 @@
namespace KonSoft.InternalGateway.Aggregations.Base;
public interface IAggregateRemoteService<TDto>
{
Task<Dictionary<string, TDto>> GetMultipleAsync(Dictionary<string, string> serviceNameWithUrlDictionary);
Task<T> MakeRequestAsync<T>(HttpClient httpClient, string url);
Task<KeyValuePair<TKey, Task<TValue>>> WaitForAnyTaskAsync<TKey, TValue>(Dictionary<TKey, Task<TValue>> tasks);
}

View File

@ -0,0 +1,7 @@
namespace KonSoft.InternalGateway.Aggregations.Base;
public interface ICachedServiceBase<TValue>
{
void Add(string serviceName, TValue data);
IDictionary<string, TValue> GetManyAsync(IEnumerable<string> serviceNames);
}

View File

@ -0,0 +1,6 @@
namespace KonSoft.InternalGateway.Aggregations.Base;
public interface IRequestInput
{
Dictionary<string, string> Endpoints { get; }
}

View File

@ -0,0 +1,10 @@
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
namespace KonSoft.InternalGateway.Aggregations.Localization;
public interface ILocalizationAggregation
{
string LocalizationRouteName { get; }
string LocalizationEndpoint { get; }
Task<ApplicationLocalizationDto> GetLocalizationAsync(LocalizationRequest input);
}

View File

@ -0,0 +1,6 @@
using KonSoft.InternalGateway.Aggregations.Base;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
namespace KonSoft.InternalGateway.Aggregations.Localization;
public interface ILocalizationRemoteService : IAggregateRemoteService<ApplicationLocalizationDto>;

View File

@ -0,0 +1,69 @@
using KonSoft.InternalGateway.Aggregations.Base;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
namespace KonSoft.InternalGateway.Aggregations.Localization;
public class LocalizationAggregation : AggregateServiceBase<ApplicationLocalizationDto>, ILocalizationAggregation,
ITransientDependency
{
public string LocalizationRouteName => "EshopOnAbpLocalization";
public string LocalizationEndpoint => "api/abp/application-localization";
protected LocalizationCachedService LocalizationCachedService { get; }
public LocalizationAggregation(
LocalizationCachedService localizationCachedService,
ILocalizationRemoteService localizationRemoteService)
: base(localizationRemoteService)
{
LocalizationCachedService = localizationCachedService;
}
public async Task<ApplicationLocalizationDto> GetLocalizationAsync(LocalizationRequest input)
{
// Check the cache service
var cachedLocalization = LocalizationCachedService
.GetManyAsync(input.Endpoints.Keys.ToArray());
// Compare cache with input service list
var missingLocalizationKeys = GetMissingServiceKeys(cachedLocalization, input.Endpoints);
if (missingLocalizationKeys.Count != 0)
{
// Make request to remote localization service to get missing localizations
var remoteLocalizationResults =
await GetMultipleFromRemoteAsync(missingLocalizationKeys, input.Endpoints);
// Update localization cache
foreach (var result in remoteLocalizationResults)
{
LocalizationCachedService.Add(result.Key, result.Value);
}
cachedLocalization = LocalizationCachedService
.GetManyAsync(input.Endpoints.Keys.ToArray());
}
//merge result
var mergedResult = MergeLocalizationData(cachedLocalization);
//return result
return mergedResult;
}
private static ApplicationLocalizationDto MergeLocalizationData(
IDictionary<string, ApplicationLocalizationDto> resourceDictionary)
{
var localizationDto = new ApplicationLocalizationDto();
foreach (var localization in resourceDictionary)
{
foreach (var resource in localization.Value.Resources)
{
localizationDto.Resources.TryAdd(resource.Key, resource.Value);
}
}
return localizationDto;
}
}

View File

@ -0,0 +1,9 @@
using KonSoft.InternalGateway.Aggregations.Base;
using Microsoft.Extensions.Caching.Memory;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
namespace KonSoft.InternalGateway.Aggregations.Localization;
public class LocalizationCachedService(IMemoryCache localizationCache)
: CachedServiceBase<ApplicationLocalizationDto>(localizationCache), ISingletonDependency;

View File

@ -0,0 +1,18 @@
using KonSoft.InternalGateway.Aggregations.Base;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace KonSoft.InternalGateway.Aggregations.Localization;
public class LocalizationRemoteService : AggregateRemoteServiceBase<ApplicationLocalizationDto>,
ILocalizationRemoteService, ITransientDependency
{
public LocalizationRemoteService(
IHttpContextAccessor httpContextAccessor,
IJsonSerializer jsonSerializer,
ILogger<AggregateRemoteServiceBase<ApplicationLocalizationDto>> logger)
: base(httpContextAccessor, jsonSerializer, logger)
{
}
}

View File

@ -0,0 +1,14 @@
using KonSoft.InternalGateway.Aggregations.Base;
namespace KonSoft.InternalGateway.Aggregations.Localization;
public class LocalizationRequest : IRequestInput
{
public Dictionary<string, string> Endpoints { get; } = new();
public string CultureName { get; set; }
public LocalizationRequest(string cultureName)
{
CultureName = cultureName;
}
}

View File

@ -1,5 +1,9 @@
using KonSoft.Admin;
using KonSoft.Shared.Hosting.AspNetCore;
using KonSoft.Shared.Hosting.Gateways;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Rewrite;
using Volo.Abp;
using Volo.Abp.Modularity;
namespace KonSoft.InternalGateway
@ -10,5 +14,71 @@ namespace KonSoft.InternalGateway
)]
public class InternalGatewayModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
SwaggerConfigurationHelper.ConfigureWithOidc(
context: context,
authority: configuration["AuthServer:Authority"]!,
scopes:
[
"Admin", "Dispatch", "Payment", "Report", "TenantManagement"
],
apiTitle: "Internal Gateway API",
discoveryEndpoint: configuration["AuthServer:MetadataAddress"]
);
context.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder
.WithOrigins(
configuration["App:CorsOrigins"]!
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.Trim().RemovePostFix("/"))
.ToArray()
)
.WithAbpExposedHeaders()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
context.Services.AddMemoryCache();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCorrelationId();
app.UseCors();
app.UseAbpRequestLocalization();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSwaggerUIWithYarp(context);
app.UseAbpSerilogEnrichers();
app.UseRewriter(new RewriteOptions()
// Regex for "", "/" and "" (whitespace)
.AddRedirect("^(|\\|\\s+)$", "/swagger"));
app.UseEndpoints(endpoints =>
{
endpoints.MapReverseProxyWithLocalization();
});
}
}
}

View File

@ -1,6 +0,0 @@
@KonSoft.InternalGateway_HostAddress = http://localhost:5090
GET {{KonSoft.InternalGateway_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -1,9 +1,6 @@
using KonSoft.InternalGateway;
using KonSoft.InternalGateway.Extensions;
using KonSoft.Shared.Hosting.AspNetCore;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using KonSoft.Shared.Hosting.Gateways;
using Serilog;
var assemblyName = typeof(Program).Assembly.GetName().Name!;
@ -11,6 +8,8 @@ SerilogConfigurationHelper.Configure(assemblyName);
try
{
Log.Information($"Starting {assemblyName}.");
var builder = WebApplication.CreateBuilder(args);
builder.Configuration
.AddAgileConfig(option =>
@ -19,30 +18,13 @@ try
});
builder.Host
.AddAppSettingsSecretsJson()
.AddYarpJson()
.UseAutofac()
.UseSerilog();
builder.Services.AddAbpSwaggerGenWithOidc(builder.Configuration["AuthServer:Authority"]!, setupAction: options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Gateway",
Version = "v1"
});
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services.AddControllers();
await builder.AddApplicationAsync<InternalGatewayModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
app.UseSwaggerUIWithYarp();
app.MapReverseProxy();
app.MapGet("/heath", () => "Online");
await app.RunAsync();
return 0;

View File

@ -1,15 +1,5 @@
{
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5090"
},
"https": {
"commandName": "Project",
"launchBrowser": true,
@ -18,35 +8,8 @@
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7264;http://localhost:5090"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTPS_PORTS": "8081",
"ASPNETCORE_HTTP_PORTS": "8080"
},
"publishAllPorts": true,
"useSSL": true
"applicationUrl": "https://localhost:7264"
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:15968",
"sslPort": 44395
}
}
"$schema": "http://json.schemastore.org/launchsettings.json"
}

View File

@ -0,0 +1,109 @@
using KonSoft.InternalGateway.Aggregations.ApplicationConfiguration;
using KonSoft.InternalGateway.Aggregations.Localization;
using Yarp.ReverseProxy.Configuration;
namespace KonSoft.InternalGateway
{
public static class ReverseProxyBuilderExtensions
{
public static ReverseProxyConventionBuilder MapReverseProxyWithLocalization(
this IEndpointRouteBuilder endpoints)
{
return endpoints.MapReverseProxy(proxyBuilder =>
{
proxyBuilder.Use(async (context, next) =>
{
var endpoint = context.GetEndpoint();
var localizationAggregation = context.RequestServices
.GetRequiredService<ILocalizationAggregation>();
var appConfigurationAggregation = context.RequestServices
.GetRequiredService<IAppConfigurationAggregation>();
// The "/api/abp/application-localization" endpoint
if (localizationAggregation.LocalizationRouteName == endpoint?.DisplayName)
{
var localizationRequestInput =
CreateLocalizationRequestInput(context, localizationAggregation.LocalizationEndpoint);
var result = await localizationAggregation.GetLocalizationAsync(localizationRequestInput);
await context.Response.WriteAsJsonAsync(result);
return;
}
// The "/api/abp/application-configuration" endpoint
if (appConfigurationAggregation.AppConfigRouteName == endpoint?.DisplayName)
{
var appConfigurationRequestInput =
CreateAppConfigurationRequestInput(context, appConfigurationAggregation.AppConfigEndpoint);
var result =
await appConfigurationAggregation.GetAppConfigurationAsync(appConfigurationRequestInput);
await context.Response.WriteAsJsonAsync(result);
return;
}
await next();
});
proxyBuilder.UseLoadBalancing();
});
}
private static AppConfigurationRequest CreateAppConfigurationRequestInput(HttpContext context,
string appConfigurationPath)
{
var proxyConfig = context.RequestServices.GetRequiredService<IProxyConfigProvider>();
var input = new AppConfigurationRequest();
string path = $"{appConfigurationPath}?includeLocalizationResources=false";
var clusterList = GetClusters(proxyConfig);
foreach (var cluster in clusterList)
{
var hostUrl = new Uri(cluster.Value.Address) + $"{path}";
// CacheKey/Endpoint dictionary key -> ex: ("Administration_AppConfig")
input.Endpoints.Add($"{cluster.Key}_AppConfig", hostUrl);
}
return input;
}
private static LocalizationRequest CreateLocalizationRequestInput(HttpContext context,
string localizationPath)
{
var proxyConfig = context.RequestServices.GetRequiredService<IProxyConfigProvider>();
context.Request.Query.TryGetValue("CultureName", out var cultureName);
var input = new LocalizationRequest(cultureName);
string path = $"{localizationPath}?cultureName={cultureName}&onlyDynamics=false";
var clusterList = GetClusters(proxyConfig);
foreach (var cluster in clusterList)
{
var hostUrl = new Uri(cluster.Value.Address) + $"{path}";
// Endpoint dictionary key -> ex: ("Administration_en")
input.Endpoints.Add($"{cluster.Key}_{cultureName}", hostUrl);
}
return input;
}
private static Dictionary<string, DestinationConfig> GetClusters(IProxyConfigProvider proxyConfig)
{
var yarpConfig = proxyConfig.GetConfig();
var routedClusters = yarpConfig.Clusters
.SelectMany(t => t.Destinations,
(clusterId, destination) => new { clusterId.ClusterId, destination.Value });
return routedClusters
.GroupBy(q => q.Value.Address)
.Select(t => t.First())
.Distinct()
.ToDictionary(k => k.ClusterId, v => v.Value);
}
}
}

View File

@ -8,6 +8,7 @@
"AuthServer": {
"Authority": "https://devauth.konsoft.top",
"RequireHttpsMetadata": true,
"SwaggerClientId": "Gateway_Swagger"
"SwaggerClientId": "Gateway_Swagger",
"MetadataAddress": "https://devauth.konsoft.top"
}
}