Browse Source

Merge branch 'main' into gterdem/angular_localization_fix

gterdem/angular_localization_fix
Galip Tolga Erdem 2 years ago
parent
commit
cc05c643f4
  1. 12
      etc/k8s/eshoponabp/charts/gateway-web/templates/gateway-web-configmap.yaml
  2. 94
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/AppConfigurationAggregation.cs
  3. 9
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/AppConfigurationCachedService.cs
  4. 15
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/AppConfigurationRemoteService.cs
  5. 9
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/AppConfigurationRequest.cs
  6. 11
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/IAppConfigurationAggregation.cs
  7. 6
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/IAppConfigurationRemoteService.cs
  8. 110
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/AggregateRemoteServiceBase.cs
  9. 34
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/AggregateServiceBase.cs
  10. 41
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/CachedServiceBase.cs
  11. 12
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/IAggregateRemoteService.cs
  12. 9
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/ICachedServiceBase.cs
  13. 8
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/IRequestInput.cs
  14. 11
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/ILocalizationAggregation.cs
  15. 6
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/ILocalizationRemoteService.cs
  16. 72
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/LocalizationAggregation.cs
  17. 9
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/LocalizationCachedService.cs
  18. 20
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/LocalizationRemoteService.cs
  19. 15
      gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/LocalizationRequest.cs
  20. 9
      gateways/web/src/EShopOnAbp.WebGateway/EShopOnAbpWebGatewayModule.cs
  21. 113
      gateways/web/src/EShopOnAbp.WebGateway/ReverseProxyBuilderExtensions.cs
  22. 12
      gateways/web/src/EShopOnAbp.WebGateway/yarp.json
  23. 9
      tye.yaml

12
etc/k8s/eshoponabp/charts/gateway-web/templates/gateway-web-configmap.yaml

@ -13,6 +13,18 @@ data:
"Path": "/api/abp/{**catch-all}"
}
},
"EshopOnAbpLocalization": {
"ClusterId": "Administration",
"Match": {
"Path": "/api/abp/application-localization"
}
},
"EshopOnAbpApplicationConfiguration": {
"ClusterId": "Administration",
"Match": {
"Path": "/api/abp/application-configuration"
}
},
"Identity Service": {
"ClusterId": "Identity",
"Match": {

94
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/AppConfigurationAggregation.cs

@ -0,0 +1,94 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.WebGateway.Aggregations.Base;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.WebGateway.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
};
}
}

9
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/AppConfigurationCachedService.cs

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

15
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/AppConfigurationRemoteService.cs

@ -0,0 +1,15 @@
using EShopOnAbp.WebGateway.Aggregations.Base;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration;
public class AppConfigurationRemoteService(
IHttpContextAccessor httpContextAccessor,
IJsonSerializer jsonSerializer,
ILogger<AggregateRemoteServiceBase<ApplicationConfigurationDto>> logger)
: AggregateRemoteServiceBase<ApplicationConfigurationDto>(httpContextAccessor, jsonSerializer, logger),
IAppConfigurationRemoteService, ITransientDependency;

9
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/AppConfigurationRequest.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
using EShopOnAbp.WebGateway.Aggregations.Base;
namespace EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration;
public class AppConfigurationRequest : IRequestInput
{
public Dictionary<string, string> Endpoints { get; } = new();
}

11
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/IAppConfigurationAggregation.cs

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
namespace EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration;
public interface IAppConfigurationAggregation
{
string AppConfigRouteName { get; }
string AppConfigEndpoint { get; }
Task<ApplicationConfigurationDto> GetAppConfigurationAsync(AppConfigurationRequest input);
}

6
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/ApplicationConfiguration/IAppConfigurationRemoteService.cs

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

110
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/AggregateRemoteServiceBase.cs

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Volo.Abp.Json;
namespace EShopOnAbp.WebGateway.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;
}
}

34
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/AggregateServiceBase.cs

@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace EShopOnAbp.WebGateway.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();
}
}

41
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/CachedServiceBase.cs

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Caching.Memory;
namespace EShopOnAbp.WebGateway.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;
}
}

12
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/IAggregateRemoteService.cs

@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
namespace EShopOnAbp.WebGateway.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);
}

9
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/ICachedServiceBase.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace EShopOnAbp.WebGateway.Aggregations.Base;
public interface ICachedServiceBase<TValue>
{
void Add(string serviceName, TValue data);
IDictionary<string, TValue> GetManyAsync(IEnumerable<string> serviceNames);
}

8
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Base/IRequestInput.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace EShopOnAbp.WebGateway.Aggregations.Base;
public interface IRequestInput
{
Dictionary<string, string> Endpoints { get; }
}

11
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/ILocalizationAggregation.cs

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
namespace EShopOnAbp.WebGateway.Aggregations.Localization;
public interface ILocalizationAggregation
{
string LocalizationRouteName { get; }
string LocalizationEndpoint { get; }
Task<ApplicationLocalizationDto> GetLocalizationAsync(LocalizationRequest input);
}

6
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/ILocalizationRemoteService.cs

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

72
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/LocalizationAggregation.cs

@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.WebGateway.Aggregations.Base;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.WebGateway.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;
}
}

9
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/LocalizationCachedService.cs

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

20
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/LocalizationRemoteService.cs

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

15
gateways/web/src/EShopOnAbp.WebGateway/Aggregations/Localization/LocalizationRequest.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
using EShopOnAbp.WebGateway.Aggregations.Base;
namespace EShopOnAbp.WebGateway.Aggregations.Localization;
public class LocalizationRequest : IRequestInput
{
public Dictionary<string, string> Endpoints { get; } = new();
public string CultureName { get; set; }
public LocalizationRequest(string cultureName)
{
CultureName = cultureName;
}
}

9
gateways/web/src/EShopOnAbp.WebGateway/EShopOnAbpWebGatewayModule.cs

@ -53,6 +53,8 @@ public class EShopOnAbpWebGatewayModule : AbpModule
.AllowCredentials();
});
});
context.Services.AddMemoryCache();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
@ -66,6 +68,8 @@ public class EShopOnAbpWebGatewayModule : AbpModule
}
app.UseCorrelationId();
app.UseCors();
app.UseAbpRequestLocalization();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
@ -76,6 +80,9 @@ public class EShopOnAbpWebGatewayModule : AbpModule
// Regex for "", "/" and "" (whitespace)
.AddRedirect("^(|\\|\\s+)$", "/swagger"));
app.UseEndpoints(endpoints => { endpoints.MapReverseProxy(); });
app.UseEndpoints(endpoints =>
{
endpoints.MapReverseProxyWithLocalization();
});
}
}

113
gateways/web/src/EShopOnAbp.WebGateway/ReverseProxyBuilderExtensions.cs

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using EShopOnAbp.WebGateway.Aggregations.ApplicationConfiguration;
using EShopOnAbp.WebGateway.Aggregations.Localization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Yarp.ReverseProxy.Configuration;
namespace EShopOnAbp.WebGateway;
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);
}
}

12
gateways/web/src/EShopOnAbp.WebGateway/yarp.json

@ -7,6 +7,18 @@
"Path": "/api/abp/{**catch-all}"
}
},
"EshopOnAbpLocalization": {
"ClusterId": "Administration",
"Match": {
"Path": "/api/abp/application-localization"
}
},
"EshopOnAbpApplicationConfiguration": {
"ClusterId": "Administration",
"Match": {
"Path": "/api/abp/application-configuration"
}
},
"Identity Service": {
"ClusterId": "Identity",
"Match": {

9
tye.yaml

@ -1,14 +1,5 @@
name: EShopOnAbp
services:
# - name: auth-server
# project: apps/auth-server/src/EShopOnAbp.AuthServer/EShopOnAbp.AuthServer.csproj
# bindings:
# - protocol: https
# port: 44330
# env:
# - Kestrel__Certificates__Default__Path=../../../../etc/dev-cert/localhost.pfx
# - Kestrel__Certificates__Default__Password=8b6039b6-c67a-448b-977b-0ce6d3fcfd49
- name: identity-service
project: services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/EShopOnAbp.IdentityService.HttpApi.Host.csproj
bindings:

Loading…
Cancel
Save