From 5f6296a324e97856ce7a01865073aab16af0e42d Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 20 May 2020 21:28:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=B7=AF=E7=94=B1=E8=81=9A?= =?UTF-8?q?=E5=90=88=E7=9A=84=E5=AE=9E=E7=8E=B0=EF=BC=8C=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=93=E7=94=A8=E4=BA=8EAbpApiDefinition?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=9A=84=E8=87=AA=E5=AE=9A=E4=B9=89=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E8=81=9A=E5=90=88=E4=B8=AD=E9=97=B4=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApiGatewayPermissionDefinitionProvider.cs | 1 + .../ApiGateway/ApiGatewayPermissions.cs | 1 + .../Localization/ApplicationContracts/en.json | 4 +- .../ApplicationContracts/zh-Hans.json | 4 +- .../Dto/AggregateReRouteConfigCreateDto.cs | 20 ++ .../AggregateReRouteConfigGetByKeyInputDto.cs | 14 + .../Ocelot/Dto/AggregateReRouteCreateDto.cs | 15 + .../Ocelot/Dto/AggregateReRouteUpdateDto.cs | 13 + .../Dto/AggregateRouteGetByRouteIdInputDto.cs | 10 + .../Dto/Base/AggregateReRouteDtoBase.cs | 4 +- .../Ocelot/Dto/Result/AggregateReRouteDto.cs | 10 + .../Ocelot/IAggregateReRouteAppService.cs | 12 + .../ApiGatewayApplicationAutoMapperProfile.cs | 18 +- .../Ocelot/AggregateReRouteAppService.cs | 121 ++++++- .../ApiGateway/Ocelot/ReRouteAppService.cs | 2 +- .../Ocelot/Aggregate/AggregateReRoute.cs | 48 ++- .../Aggregate/IAggregateReRouteRepository.cs | 4 + .../EfCoreAggregateReRouteRepository.cs | 10 + .../Ocelot/AggregateReRouteController.cs | 39 +++ ...antManagement.Application.Contracts.csproj | 5 +- ...INGYUN.TenantManagement.Application.csproj | 8 + ....Application.csprojAssemblyReference.cache | Bin 424 -> 144132 bytes .../ApiGateway/ApiGatewayHostModule.cs | 6 +- .../AbpApiDefinitionResponseAggregator.cs | 152 +++++++++ vueJs/src/api/apigateway.ts | 141 +++++++++ vueJs/src/lang/zh.ts | 17 +- vueJs/src/router/index.ts | 10 + .../views/admin/apigateway/aggregateRoute.vue | 295 ++++++++++++++++++ .../AggregateRouteCreateOrEditForm.vue | 197 ++++++++++++ .../components/RouteCreateOrEditForm.vue | 1 - 30 files changed, 1150 insertions(+), 32 deletions(-) create mode 100644 aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteConfigCreateDto.cs create mode 100644 aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteConfigGetByKeyInputDto.cs create mode 100644 aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteCreateDto.cs create mode 100644 aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteUpdateDto.cs create mode 100644 aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateRouteGetByRouteIdInputDto.cs create mode 100644 aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/Ocelot/Middleware/Multiplexer/AbpApiDefinitionResponseAggregator.cs create mode 100644 vueJs/src/views/admin/apigateway/components/AggregateRouteCreateOrEditForm.vue diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/ApiGatewayPermissionDefinitionProvider.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/ApiGatewayPermissionDefinitionProvider.cs index e9e5ba4d9..878780cf1 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/ApiGatewayPermissionDefinitionProvider.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/ApiGatewayPermissionDefinitionProvider.cs @@ -45,6 +45,7 @@ namespace LINGYUN.ApiGateway aggregateRoute.AddChild(ApiGatewayPermissions.AggregateRoute.Export, L("Permissions:Export"), MultiTenancySides.Host); aggregateRoute.AddChild(ApiGatewayPermissions.AggregateRoute.Import, L("Permissions:Import"), MultiTenancySides.Host); aggregateRoute.AddChild(ApiGatewayPermissions.AggregateRoute.Delete, L("Permissions:Delete"), MultiTenancySides.Host); + aggregateRoute.AddChild(ApiGatewayPermissions.AggregateRoute.ManageRouteConfig, L("Permissions:ManageRouteConfig"), MultiTenancySides.Host); } protected virtual LocalizableString L(string name) diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/ApiGatewayPermissions.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/ApiGatewayPermissions.cs index ca9e091f5..7289b771f 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/ApiGatewayPermissions.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/ApiGatewayPermissions.cs @@ -51,6 +51,7 @@ public const string Delete = Default + ".Delete"; public const string Export = Default + ".Export"; public const string Import = Default + ".Import"; + public const string ManageRouteConfig = Default + ".ManageRouteConfig"; } } } diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Localization/ApplicationContracts/en.json b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Localization/ApplicationContracts/en.json index 5c7fbad97..3f738e9c2 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Localization/ApplicationContracts/en.json +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Localization/ApplicationContracts/en.json @@ -7,9 +7,11 @@ "Permissions:Route": "Route", "Permissions:DynamicRoute": "Dynamic", "Permissions:AggregateRoute": "Aggregate", + "Permissions:ManageRouteConfig": "Route config", "Permissions:Update": "Update", "Permissions:Export": "Export", "Permissions:Import": "Import", - "Permissions:Delete": "Delete" + "Permissions:Delete": "Delete", + "AggregateReRouteExists": "Aggregate name: {0} already exists!" } } \ No newline at end of file diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Localization/ApplicationContracts/zh-Hans.json b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Localization/ApplicationContracts/zh-Hans.json index 583a6da24..3866b0ed1 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Localization/ApplicationContracts/zh-Hans.json +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Localization/ApplicationContracts/zh-Hans.json @@ -7,10 +7,12 @@ "Permissions:Route": "路由配置", "Permissions:DynamicRoute": "动态路由", "Permissions:AggregateRoute": "路由聚合", + "Permissions:ManageRouteConfig": "管理路由配置", "Permissions:Create": "新增", "Permissions:Update": "编辑", "Permissions:Export": "导出", "Permissions:Import": "导入", - "Permissions:Delete": "删除" + "Permissions:Delete": "删除", + "AggregateReRouteExists": "聚合名称: {0} 已存在!" } } \ No newline at end of file diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteConfigCreateDto.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteConfigCreateDto.cs new file mode 100644 index 000000000..5e7a8830e --- /dev/null +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteConfigCreateDto.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.ApiGateway.Ocelot +{ + public class AggregateReRouteConfigCreateDto + { + [Required] + public string RouteId { get; set; } + + [Required] + [StringLength(256)] + public string ReRouteKey { get; set; } + + [StringLength(1000)] + public string Parameter { get; set; } + + [StringLength(256)] + public string JsonPath { get; set; } + } +} diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteConfigGetByKeyInputDto.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteConfigGetByKeyInputDto.cs new file mode 100644 index 000000000..561d1408a --- /dev/null +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteConfigGetByKeyInputDto.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.ApiGateway.Ocelot +{ + public class AggregateReRouteConfigGetByKeyInputDto + { + [Required] + public string RouteId { get; set; } + + [Required] + [StringLength(256)] + public string ReRouteKey { get; set; } + } +} diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteCreateDto.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteCreateDto.cs new file mode 100644 index 000000000..59cdba0ce --- /dev/null +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteCreateDto.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.ApiGateway.Ocelot +{ + public class AggregateReRouteCreateDto : AggregateReRouteDtoBase + { + [Required] + [StringLength(50)] + public string AppId { get; set; } + + [Required] + [StringLength(50)] + public string Name { get; set; } + } +} diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteUpdateDto.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteUpdateDto.cs new file mode 100644 index 000000000..c4729d6f3 --- /dev/null +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateReRouteUpdateDto.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.ApiGateway.Ocelot +{ + public class AggregateReRouteUpdateDto : AggregateReRouteDtoBase + { + [Required] + public string RouteId { get; set; } + + [Required] + public string ConcurrencyStamp { get; set; } + } +} diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateRouteGetByRouteIdInputDto.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateRouteGetByRouteIdInputDto.cs new file mode 100644 index 000000000..3e1a3c5c3 --- /dev/null +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/AggregateRouteGetByRouteIdInputDto.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.ApiGateway.Ocelot +{ + public class AggregateRouteGetByRouteIdInputDto + { + [Required] + public string RouteId { get; set; } + } +} diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/Base/AggregateReRouteDtoBase.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/Base/AggregateReRouteDtoBase.cs index 16f3448c7..c58e30024 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/Base/AggregateReRouteDtoBase.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/Base/AggregateReRouteDtoBase.cs @@ -7,18 +7,16 @@ namespace LINGYUN.ApiGateway.Ocelot public class AggregateReRouteDtoBase { public List ReRouteKeys { get; set; } - public List ReRouteKeysConfig { get; set; } public string UpstreamPathTemplate { get; set; } public string UpstreamHost { get; set; } public bool ReRouteIsCaseSensitive { get; set; } public string Aggregator { get; set; } - public int Priority { get; set; } + public int? Priority { get; set; } public List UpstreamHttpMethod { get; set; } public AggregateReRouteDtoBase() { ReRouteKeys = new List(); UpstreamHttpMethod = new List(); - ReRouteKeysConfig = new List(); } } } diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/Result/AggregateReRouteDto.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/Result/AggregateReRouteDto.cs index 53ead89cd..816c6b154 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/Result/AggregateReRouteDto.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/Dto/Result/AggregateReRouteDto.cs @@ -1,16 +1,26 @@ using Newtonsoft.Json; using System; +using System.Collections.Generic; namespace LINGYUN.ApiGateway.Ocelot { [Serializable] public class AggregateReRouteDto : AggregateReRouteDtoBase { + public string AppId { get; set; } + [JsonConverter(typeof(HexLongConverter))] public long ReRouteId { get; set; } + public string Name { get; set; } + + public string ConcurrencyStamp { get; set; } + + public List ReRouteKeysConfig { get; set; } + public AggregateReRouteDto() { + ReRouteKeysConfig = new List(); } } } diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/IAggregateReRouteAppService.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/IAggregateReRouteAppService.cs index fdfca3348..e5b788ca4 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/IAggregateReRouteAppService.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application.Contracts/LINGYUN/ApiGateway/Ocelot/IAggregateReRouteAppService.cs @@ -6,8 +6,20 @@ namespace LINGYUN.ApiGateway.Ocelot { public interface IAggregateReRouteAppService : IApplicationService { + Task GetAsync(AggregateRouteGetByRouteIdInputDto aggregateRouteGetByRouteId); + Task> GetAsync(AggregateRouteGetByAppIdInputDto aggregateRouteGetByAppId); Task> GetPagedListAsync(AggregateRouteGetByPagedInputDto aggregateRouteGetByPaged); + + Task CreateAsync(AggregateReRouteCreateDto aggregateReRouteCreate); + + Task UpdateAsync(AggregateReRouteUpdateDto aggregateReRouteUpdate); + + Task DeleteAsync(AggregateRouteGetByRouteIdInputDto aggregateRouteGetByRouteId); + + Task AddRouteConfigAsync(AggregateReRouteConfigCreateDto aggregateReRouteConfigCreate); + + Task DeleteRouteConfigAsync(AggregateReRouteConfigGetByKeyInputDto aggregateReRouteConfigGetByKey); } } diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/ApiGatewayApplicationAutoMapperProfile.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/ApiGatewayApplicationAutoMapperProfile.cs index c898fc2ec..1ddc617f4 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/ApiGatewayApplicationAutoMapperProfile.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/ApiGatewayApplicationAutoMapperProfile.cs @@ -64,12 +64,18 @@ namespace LINGYUN.ApiGateway CreateMap(); CreateMap() - .ForMember(dto => dto.ReRouteKeys, map => map.MapFrom(m => !m.ReRouteKeys.IsNullOrWhiteSpace() && m.ReRouteKeys.Contains(",") - ? m.ReRouteKeys.Split(',').ToList() - : new List())) - .ForMember(dto => dto.UpstreamHttpMethod, map => map.MapFrom(m => !m.UpstreamHttpMethod.IsNullOrWhiteSpace() && m.UpstreamHttpMethod.Contains(",") - ? m.UpstreamHttpMethod.Split(',').ToList() - : new List())); + .ForMember(dto => dto.ReRouteKeys, map => map.MapFrom(m => MapperList(m.ReRouteKeys))) + .ForMember(dto => dto.UpstreamHttpMethod, map => map.MapFrom(m => MapperList(m.UpstreamHttpMethod))); + + CreateMap() + .ForCtorParam("name", x => x.MapFrom(m => m.Name)) + .ForCtorParam("routeId", x => x.MapFrom(m => snowflakeIdGenerator.NextId())) + .ForCtorParam("aggregator", x => x.MapFrom(m => m.Aggregator)) + .ForCtorParam("appId", x => x.MapFrom(m => m.AppId)) + .ForMember(src => src.ExtraProperties, x => x.Ignore()) + .ForMember(src => src.ReRouteKeys, x => x.Ignore()) + .ForMember(src => src.ReRouteKeysConfig, x => x.Ignore()) + .ForMember(src => src.UpstreamHttpMethod, x => x.Ignore()); CreateMap(); diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/AggregateReRouteAppService.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/AggregateReRouteAppService.cs index 78e28383b..1f742719e 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/AggregateReRouteAppService.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/AggregateReRouteAppService.cs @@ -1,21 +1,40 @@ -using Microsoft.AspNetCore.Authorization; +using DotNetCore.CAP; +using LINGYUN.ApiGateway.EventBus; +using LINGYUN.ApiGateway.Snowflake; +using Microsoft.AspNetCore.Authorization; using System.Collections.Generic; using System.Threading.Tasks; +using Volo.Abp; using Volo.Abp.Application.Dtos; namespace LINGYUN.ApiGateway.Ocelot { + // 2020-05-20 15:00 + // TODO 重构项目 规范化实体的存储形式,数组类型分为多张表存储 + // TODO 取消long类型的实体主键 改用GUID + [Authorize(ApiGatewayPermissions.AggregateRoute.Default)] public class AggregateReRouteAppService : ApiGatewayApplicationServiceBase, IAggregateReRouteAppService { + private readonly ICapPublisher _eventPublisher; private readonly IAggregateReRouteRepository _aggregateReRouteRepository; public AggregateReRouteAppService( + ICapPublisher eventPublisher, IAggregateReRouteRepository aggregateReRouteRepository) { + _eventPublisher = eventPublisher; _aggregateReRouteRepository = aggregateReRouteRepository; } + public virtual async Task GetAsync(AggregateRouteGetByRouteIdInputDto aggregateRouteGetByRouteId) + { + var routeId = long.Parse(aggregateRouteGetByRouteId.RouteId); + var reroute = await _aggregateReRouteRepository.GetByRouteIdAsync(routeId); + + return ObjectMapper.Map(reroute); + } + [Authorize(ApiGatewayPermissions.AggregateRoute.Export)] public async Task> GetAsync(AggregateRouteGetByAppIdInputDto aggregateRouteGetByAppId) { @@ -34,5 +53,105 @@ namespace LINGYUN.ApiGateway.Ocelot return new PagedResultDto(reroutesTuple.total, ObjectMapper.Map, List>(reroutesTuple.routes)); } + + [Authorize(ApiGatewayPermissions.AggregateRoute.Create)] + public virtual async Task CreateAsync(AggregateReRouteCreateDto aggregateReRouteCreate) + { + var aggregateNameExists = await _aggregateReRouteRepository + .AggregateReRouteNameExistsAsync(aggregateReRouteCreate.Name); + if (aggregateNameExists) + { + throw new UserFriendlyException(L["AggregateReRouteExists", aggregateReRouteCreate.Name]); + } + var aggregateRoute = ObjectMapper.Map(aggregateReRouteCreate); + aggregateRoute.SetUpstream(aggregateReRouteCreate.UpstreamHost, aggregateReRouteCreate.UpstreamPathTemplate); + foreach (var httpMethod in aggregateReRouteCreate.UpstreamHttpMethod) + { + aggregateRoute.AddUpstreamHttpMethod(httpMethod); + } + foreach (var routeKey in aggregateReRouteCreate.ReRouteKeys) + { + aggregateRoute.AddRouteKey(routeKey); + } + aggregateRoute = await _aggregateReRouteRepository.InsertAsync(aggregateRoute); + + await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, + new ApigatewayConfigChangeCommand("AggregateRoute", "Create")); + + return ObjectMapper.Map(aggregateRoute); + } + + [Authorize(ApiGatewayPermissions.AggregateRoute.Update)] + public virtual async Task UpdateAsync(AggregateReRouteUpdateDto aggregateReRouteUpdate) + { + var routeId = long.Parse(aggregateReRouteUpdate.RouteId); + var aggregateRoute = await _aggregateReRouteRepository.GetByRouteIdAsync(routeId); + aggregateRoute.Priority = aggregateReRouteUpdate.Priority; + aggregateRoute.ConcurrencyStamp = aggregateReRouteUpdate.ConcurrencyStamp; + aggregateRoute.ReRouteIsCaseSensitive = aggregateReRouteUpdate.ReRouteIsCaseSensitive; + aggregateRoute.Aggregator = aggregateReRouteUpdate.Aggregator; + aggregateRoute.SetUpstream(aggregateReRouteUpdate.UpstreamHost, aggregateReRouteUpdate.UpstreamPathTemplate); + + aggregateRoute.RemoveAllUpstreamHttpMethod(); + foreach (var httpMethod in aggregateReRouteUpdate.UpstreamHttpMethod) + { + aggregateRoute.AddUpstreamHttpMethod(httpMethod); + } + + aggregateRoute.RemoveAllRouteKey(); + foreach (var routeKey in aggregateReRouteUpdate.ReRouteKeys) + { + aggregateRoute.AddRouteKey(routeKey); + } + + aggregateRoute = await _aggregateReRouteRepository.UpdateAsync(aggregateRoute, true); + + await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, + new ApigatewayConfigChangeCommand("AggregateRoute", "Update")); + + return ObjectMapper.Map(aggregateRoute); + } + + [Authorize(ApiGatewayPermissions.AggregateRoute.Delete)] + public virtual async Task DeleteAsync(AggregateRouteGetByRouteIdInputDto aggregateRouteGetByRouteId) + { + var routeId = long.Parse(aggregateRouteGetByRouteId.RouteId); + var aggregateRoute = await _aggregateReRouteRepository.GetByRouteIdAsync(routeId); + await _aggregateReRouteRepository.DeleteAsync(aggregateRoute); + + await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, + new ApigatewayConfigChangeCommand("AggregateRoute", "Delete")); + } + + [Authorize(ApiGatewayPermissions.AggregateRoute.ManageRouteConfig)] + public virtual async Task AddRouteConfigAsync(AggregateReRouteConfigCreateDto aggregateReRouteConfigCreate) + { + var routeId = long.Parse(aggregateReRouteConfigCreate.RouteId); + var aggregateRoute = await _aggregateReRouteRepository.GetByRouteIdAsync(routeId); + aggregateRoute.RemoveReRouteConfig(aggregateReRouteConfigCreate.ReRouteKey) + .AddReRouteConfig(aggregateReRouteConfigCreate.ReRouteKey, aggregateReRouteConfigCreate.Parameter, + aggregateReRouteConfigCreate.JsonPath); + var aggregateRouteConfig = aggregateRoute.FindReRouteConfig(aggregateReRouteConfigCreate.ReRouteKey); + + await _aggregateReRouteRepository.UpdateAsync(aggregateRoute); + + await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, + new ApigatewayConfigChangeCommand("AggregateRoute", "AddRouteConfig")); + + return ObjectMapper.Map(aggregateRouteConfig); + } + + [Authorize(ApiGatewayPermissions.AggregateRoute.ManageRouteConfig)] + public virtual async Task DeleteRouteConfigAsync(AggregateReRouteConfigGetByKeyInputDto aggregateReRouteConfigGetByKey) + { + var routeId = long.Parse(aggregateReRouteConfigGetByKey.RouteId); + var aggregateRoute = await _aggregateReRouteRepository.GetByRouteIdAsync(routeId); + aggregateRoute.RemoveReRouteConfig(aggregateReRouteConfigGetByKey.ReRouteKey); + + await _aggregateReRouteRepository.UpdateAsync(aggregateRoute); + + await _eventPublisher.PublishAsync(ApigatewayConfigChangeCommand.EventName, + new ApigatewayConfigChangeCommand("AggregateRoute", "DeleteRouteConfig")); + } } } diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/ReRouteAppService.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/ReRouteAppService.cs index 643002669..03f81cb6c 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/ReRouteAppService.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Application/LINGYUN/ApiGateway/Ocelot/ReRouteAppService.cs @@ -46,7 +46,7 @@ namespace LINGYUN.ApiGateway.Ocelot public async Task UpdateAsync(ReRouteUpdateDto routeUpdateDto) { var reRoute = await _reRouteRepository.GetByReRouteIdAsync(long.Parse(routeUpdateDto.ReRouteId)); - + reRoute.SetRouteName(routeUpdateDto.ReRouteName); reRoute.DangerousAcceptAnyServerCertificateValidator = routeUpdateDto.DangerousAcceptAnyServerCertificateValidator; reRoute.DownstreamScheme = routeUpdateDto.DownstreamScheme; reRoute.Key = routeUpdateDto.Key; diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN/ApiGateway/Ocelot/Aggregate/AggregateReRoute.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN/ApiGateway/Ocelot/Aggregate/AggregateReRoute.cs index fd524324e..393326a0e 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN/ApiGateway/Ocelot/Aggregate/AggregateReRoute.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN/ApiGateway/Ocelot/Aggregate/AggregateReRoute.cs @@ -16,8 +16,8 @@ namespace LINGYUN.ApiGateway.Ocelot public virtual string ReRouteKeys { get; private set; } public virtual string UpstreamPathTemplate { get; private set; } public virtual string UpstreamHost { get; private set; } - public virtual bool ReRouteIsCaseSensitive { get; private set; } - public virtual string Aggregator { get; private set; } + public virtual bool ReRouteIsCaseSensitive { get; set; } + public virtual string Aggregator { get; set; } public virtual int? Priority { get; set; } public virtual string UpstreamHttpMethod { get; private set; } public virtual ICollection ReRouteKeysConfig { get; private set; } @@ -26,26 +26,27 @@ namespace LINGYUN.ApiGateway.Ocelot ReRouteKeysConfig = new List(); } - public AggregateReRoute SetUpstreamPath(string host, string path, string appId) + public AggregateReRoute(string name, long routeId, string aggregator, string appId) : this() { AppId = appId; - UpstreamHost = host; - UpstreamPathTemplate = path; - return this; - } - - public AggregateReRoute(string name, long routeId, string aggregator) : this() - { Name = name; ReRouteId = routeId; Aggregator = aggregator; + ReRouteKeys = ""; + UpstreamHttpMethod = ""; + } + + public void SetUpstream(string host, string template) + { + UpstreamHost = host; + UpstreamPathTemplate = template; } public AggregateReRoute AddUpstreamHttpMethod(string method) { if (!UpstreamHttpMethod.Contains(method)) { - UpstreamHttpMethod += "," + method; + UpstreamHttpMethod += method + ","; } return this; } @@ -60,11 +61,17 @@ namespace LINGYUN.ApiGateway.Ocelot return this; } + public AggregateReRoute RemoveAllUpstreamHttpMethod() + { + UpstreamHttpMethod = ""; + return this; + } + public AggregateReRoute AddRouteKey(string key) { if (!ReRouteKeys.Contains(key)) { - ReRouteKeys += "," + key; + ReRouteKeys += key + ","; } return this; } @@ -79,6 +86,17 @@ namespace LINGYUN.ApiGateway.Ocelot return this; } + public AggregateReRoute RemoveAllRouteKey() + { + ReRouteKeys = ""; + return this; + } + + public AggregateReRouteConfig FindReRouteConfig(string routeKey) + { + return ReRouteKeysConfig.FirstOrDefault(cfg => cfg.ReRouteKey.Equals(routeKey)); + } + public AggregateReRoute AddReRouteConfig(string routeKey, string paramter, string jsonPath) { if (!ReRouteKeysConfig.Any(k => k.ReRouteKey.Equals(routeKey))) @@ -95,5 +113,11 @@ namespace LINGYUN.ApiGateway.Ocelot ReRouteKeysConfig.RemoveAll(k => k.ReRouteKey.Equals(routeKey)); return this; } + + public AggregateReRoute RemoveAllReRouteConfig() + { + ReRouteKeysConfig.Clear(); + return this; + } } } diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN/ApiGateway/Ocelot/Aggregate/IAggregateReRouteRepository.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN/ApiGateway/Ocelot/Aggregate/IAggregateReRouteRepository.cs index 79191a444..c350591e1 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN/ApiGateway/Ocelot/Aggregate/IAggregateReRouteRepository.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.Domain/LINGYUN/ApiGateway/Ocelot/Aggregate/IAggregateReRouteRepository.cs @@ -6,6 +6,10 @@ namespace LINGYUN.ApiGateway.Ocelot { public interface IAggregateReRouteRepository : IBasicRepository { + Task AggregateReRouteNameExistsAsync(string name); + + Task GetByRouteIdAsync(long routeId); + Task> GetByAppIdAsync(string appId); Task<(List routes, long total)> GetPagedListAsync(string appId, string filter = "", string sorting = "", int skipCount = 1, int maxResultCount = 100); diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.EntityFrameworkCore/LINGYUN/ApiGateway/Ocelot/EfCoreAggregateReRouteRepository.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.EntityFrameworkCore/LINGYUN/ApiGateway/Ocelot/EfCoreAggregateReRouteRepository.cs index fa69a11d3..1320dda17 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.EntityFrameworkCore/LINGYUN/ApiGateway/Ocelot/EfCoreAggregateReRouteRepository.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.EntityFrameworkCore/LINGYUN/ApiGateway/Ocelot/EfCoreAggregateReRouteRepository.cs @@ -15,6 +15,16 @@ namespace LINGYUN.ApiGateway.Ocelot { } + public async Task AggregateReRouteNameExistsAsync(string name) + { + return await DbSet.AnyAsync(ar => ar.Name.Equals(name)); + } + + public async Task GetByRouteIdAsync(long routeId) + { + return await WithDetails().Where(ar => ar.ReRouteId.Equals(routeId)).FirstOrDefaultAsync(); + } + public async Task> GetByAppIdAsync(string appId) { return await WithDetails().Where(ar => ar.AppId.Equals(appId)).ToListAsync(); diff --git a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.HttpApi/LINGYUN/ApiGateway/Ocelot/AggregateReRouteController.cs b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.HttpApi/LINGYUN/ApiGateway/Ocelot/AggregateReRouteController.cs index 1ba22e8b5..e43842b8d 100644 --- a/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.HttpApi/LINGYUN/ApiGateway/Ocelot/AggregateReRouteController.cs +++ b/aspnet-core/modules/apigateway/LINGYUN.ApiGateway.HttpApi/LINGYUN/ApiGateway/Ocelot/AggregateReRouteController.cs @@ -29,5 +29,44 @@ namespace LINGYUN.ApiGateway.Ocelot { return await AggregateReRouteAppService.GetPagedListAsync(aggregateRouteGetByPaged); } + + [HttpGet] + [Route("{RouteId}")] + public async Task GetAsync(AggregateRouteGetByRouteIdInputDto aggregateRouteGetByRouteId) + { + return await AggregateReRouteAppService.GetAsync(aggregateRouteGetByRouteId); + } + + [HttpPost] + public async Task CreateAsync(AggregateReRouteCreateDto aggregateReRouteCreate) + { + return await AggregateReRouteAppService.CreateAsync(aggregateReRouteCreate); + } + + [HttpPut] + public async Task UpdateAsync(AggregateReRouteUpdateDto aggregateReRouteUpdate) + { + return await AggregateReRouteAppService.UpdateAsync(aggregateReRouteUpdate); + } + + [HttpDelete] + public async Task DeleteAsync(AggregateRouteGetByRouteIdInputDto aggregateRouteGetByRouteId) + { + await AggregateReRouteAppService.DeleteAsync(aggregateRouteGetByRouteId); + } + + [HttpPost] + [Route("RouteConfig")] + public async Task AddRouteConfigAsync(AggregateReRouteConfigCreateDto aggregateReRouteConfigCreate) + { + return await AggregateReRouteAppService.AddRouteConfigAsync(aggregateReRouteConfigCreate); + } + + [HttpDelete] + [Route("RouteConfig")] + public async Task DeleteRouteConfigAsync(AggregateReRouteConfigGetByKeyInputDto aggregateReRouteConfigGetByKey) + { + await AggregateReRouteAppService.DeleteRouteConfigAsync(aggregateReRouteConfigGetByKey); + } } } diff --git a/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application.Contracts/LINGYUN.TenantManagement.Application.Contracts.csproj b/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application.Contracts/LINGYUN.TenantManagement.Application.Contracts.csproj index 4d405ac17..b631f522e 100644 --- a/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application.Contracts/LINGYUN.TenantManagement.Application.Contracts.csproj +++ b/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application.Contracts/LINGYUN.TenantManagement.Application.Contracts.csproj @@ -5,12 +5,9 @@ - - - - + diff --git a/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application/LINGYUN.TenantManagement.Application.csproj b/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application/LINGYUN.TenantManagement.Application.csproj index 83d348032..a0fdc715c 100644 --- a/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application/LINGYUN.TenantManagement.Application.csproj +++ b/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application/LINGYUN.TenantManagement.Application.csproj @@ -5,4 +5,12 @@ + + + + + + + + diff --git a/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application/obj/Debug/netstandard2.0/LINGYUN.TenantManagement.Application.csprojAssemblyReference.cache b/aspnet-core/modules/tenants/LINGYUN.TenantManagement.Application/obj/Debug/netstandard2.0/LINGYUN.TenantManagement.Application.csprojAssemblyReference.cache index 79a6a84b13ca396196465ea95f25ce41b3a90282..2b5c4715bf485452eed4ade1792d5fb8b93c1218 100644 GIT binary patch literal 144132 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�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 } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/Ocelot/Middleware/Multiplexer/AbpApiDefinitionResponseAggregator.cs b/aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/Ocelot/Middleware/Multiplexer/AbpApiDefinitionResponseAggregator.cs new file mode 100644 index 000000000..651dffc9d --- /dev/null +++ b/aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/Ocelot/Middleware/Multiplexer/AbpApiDefinitionResponseAggregator.cs @@ -0,0 +1,152 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; + +namespace Ocelot.Middleware.Multiplexer +{ + public class AbpApiDefinitionAggregator : IDefinedAggregator + { + public async Task Aggregate(List responses) + { + var isAbpResponse = responses.Any(response => response.DownstreamResponse.Headers.Any(h => h.Key.Equals("_abperrorformat"))); + return await MapAbpApiDefinitionAggregateContentAsync(responses); + //if (isAbpResponse) + //{ + // return await MapAbpApiDefinitionAggregateContentAsync(responses); + //} + //else + //{ + // return await MapSimpleJsonAggregateContentAsync(responses); + //} + } + + protected virtual async Task MapAbpApiDefinitionAggregateContentAsync(List downstreamContexts) + { + var responseKeys = downstreamContexts.Select(s => s.DownstreamReRoute.Key).Distinct().ToList(); + JObject responseObject = null; + for (var k = 0; k < responseKeys.Count; k++) + { + var contexts = downstreamContexts.Where(w => w.DownstreamReRoute.Key == responseKeys[k]).ToList(); + if (contexts.Count == 1) + { + if (contexts[0].IsError) + { + return contexts[0].DownstreamResponse; + } + + var content = await contexts[0].DownstreamResponse.Content.ReadAsStringAsync(); + var contentObject = JsonConvert.DeserializeObject(content); + if (responseObject == null) + { + responseObject = JObject.FromObject(contentObject); + } + else + { + responseObject.Merge(contentObject); + } + } + else + { + for (var i = 0; i < contexts.Count; i++) + { + if (contexts[i].IsError) + { + return contexts[i].DownstreamResponse; + } + + var content = await contexts[i].DownstreamResponse.Content.ReadAsStringAsync(); + var contentObject = JsonConvert.DeserializeObject(content); + if (responseObject == null) + { + responseObject = JObject.FromObject(contentObject); + } + else + { + responseObject.Merge(contentObject); + } + } + } + } + + var stringContent = new StringContent(responseObject.ToString()) + { + Headers = { ContentType = new MediaTypeHeaderValue("application/json") } + }; + stringContent.Headers.Add("_abperrorformat", "true"); + return new DownstreamResponse(stringContent, HttpStatusCode.OK, new List>>(), "cannot return from aggregate..which reason phrase would you use?"); + } + + protected virtual async Task MapSimpleJsonAggregateContentAsync(List downstreamContexts) + { + var contentBuilder = new StringBuilder(); + + contentBuilder.Append("{"); + + var responseKeys = downstreamContexts.Select(s => s.DownstreamReRoute.Key).Distinct().ToList(); + + for (var k = 0; k < responseKeys.Count; k++) + { + var contexts = downstreamContexts.Where(w => w.DownstreamReRoute.Key == responseKeys[k]).ToList(); + if (contexts.Count == 1) + { + if (contexts[0].IsError) + { + return contexts[0].DownstreamResponse; + } + + var content = await contexts[0].DownstreamResponse.Content.ReadAsStringAsync(); + contentBuilder.Append($"\"{responseKeys[k]}\":{content}"); + } + else + { + contentBuilder.Append($"\"{responseKeys[k]}\":"); + contentBuilder.Append("["); + + for (var i = 0; i < contexts.Count; i++) + { + if (contexts[i].IsError) + { + return contexts[i].DownstreamResponse; + } + + var content = await contexts[i].DownstreamResponse.Content.ReadAsStringAsync(); + if (string.IsNullOrWhiteSpace(content)) + { + continue; + } + + contentBuilder.Append($"{content}"); + + if (i + 1 < contexts.Count) + { + contentBuilder.Append(","); + } + } + + contentBuilder.Append("]"); + } + + if (k + 1 < responseKeys.Count) + { + contentBuilder.Append(","); + } + } + + contentBuilder.Append("}"); + + var stringContent = new StringContent(contentBuilder.ToString()) + { + Headers = { ContentType = new MediaTypeHeaderValue("application/json") } + }; + + return new DownstreamResponse(stringContent, HttpStatusCode.OK, new List>>(), "cannot return from aggregate..which reason phrase would you use?"); + } + + } +} diff --git a/vueJs/src/api/apigateway.ts b/vueJs/src/api/apigateway.ts index 72efb3516..801abb2b5 100644 --- a/vueJs/src/api/apigateway.ts +++ b/vueJs/src/api/apigateway.ts @@ -120,6 +120,50 @@ export default class ApiGateWay { _url += '?appId=' + appId return ApiService.Delete(_url, serviceUrl) } + + public static getAggregateReRoutes(payload: AggregateReRouteGetByPaged) { + let _url = '/api/ApiGateway/Aggregates' + _url += '?appId=' + payload.appId + _url += '&filter=' + payload.filter + _url += '&sorting=' + payload.sorting + _url += '&skipCount=' + payload.skipCount + _url += '&maxResultCount=' + payload.maxResultCount + return ApiService.Get>(_url, serviceUrl) + } + + public static getAggregateReRouteByRouteId(routeId: string) { + let _url = '/api/ApiGateway/Aggregates/' + _url += routeId + return ApiService.Get(_url, serviceUrl) + } + + public static createAggregateReRoute(payload: AggregateReRouteCreate) { + const _url = '/api/ApiGateway/Aggregates' + return ApiService.Post(_url, payload, serviceUrl) + } + + public static updateAggregateReRoute(payload: AggregateReRouteUpdate) { + const _url = '/api/ApiGateway/Aggregates' + return ApiService.Put(_url, payload, serviceUrl) + } + + public static deleteAggregateReRoute(routeId: string) { + let _url = '/api/ApiGateway/Aggregates' + _url += '?routeId=' + routeId + return ApiService.Delete(_url, serviceUrl) + } + + public static createAggregateRouteConfig(payload: AggregateReRouteConfigCreate) { + const _url = '/api/ApiGateway/Aggregates/RouteConfig' + return ApiService.Post(_url, payload, serviceUrl) + } + + public static deleteAggregateRouteConfig(routeId: string, routeKey: string) { + let _url = '/api/ApiGateway/Aggregates/RouteConfig' + _url += '?routeId=' + routeId + _url += '&ReRouteKey=' + routeKey + return ApiService.Delete(_url, serviceUrl) + } } export class ServiceDiscoveryProvider { @@ -412,3 +456,100 @@ export class ReRouteGetByPagedDto extends PagedAndSortedResultRequestDto { this.sorting = 'ReRouteName' } } + +export class AggregateReRouteConfig { + reRouteKey = '' + parameter = '' + jsonPath = '' +} + +export class AggregateReRouteConfigCreate extends AggregateReRouteConfig { + routeId = '' +} + +export class AggregateReRouteBase { + reRouteKeys!: string[] + upstreamPathTemplate = '' + upstreamHost = '' + reRouteIsCaseSensitive = true + aggregator = '' + priority?: number + upstreamHttpMethod!: string[] + + constructor() { + this.reRouteKeys = new Array() + this.upstreamHttpMethod = new Array() + } +} + +export class AggregateReRoute extends AggregateReRouteBase { + appId = '' + name = '' + reRouteId = '' + concurrencyStamp = '' + reRouteKeysConfig!: AggregateReRouteConfig[] + + constructor() { + super() + this.reRouteKeysConfig = new Array() + } + + public static empty() { + return new AggregateReRoute() + } +} + +export class AggregateReRouteCreate extends AggregateReRouteBase { + appId = '' + name = '' + + public static empty() { + return new AggregateReRouteCreate() + } + + public static create(route: AggregateReRoute) { + const aggregateRoute = new AggregateReRouteCreate() + aggregateRoute.appId = route.appId + aggregateRoute.name = route.name + aggregateRoute.aggregator = route.aggregator + aggregateRoute.priority = route.priority + aggregateRoute.reRouteIsCaseSensitive = route.reRouteIsCaseSensitive + aggregateRoute.reRouteKeys = route.reRouteKeys + aggregateRoute.upstreamHost = route.upstreamHost + aggregateRoute.upstreamHttpMethod = route.upstreamHttpMethod + aggregateRoute.upstreamPathTemplate = route.upstreamPathTemplate + return aggregateRoute + } +} + +export class AggregateReRouteUpdate extends AggregateReRouteBase { + routeId = '' + concurrencyStamp = '' + + public static empty() { + return new AggregateReRouteUpdate() + } + + public static create(route: AggregateReRoute) { + const aggregateRoute = new AggregateReRouteUpdate() + aggregateRoute.aggregator = route.aggregator + aggregateRoute.concurrencyStamp = route.concurrencyStamp + aggregateRoute.priority = route.priority + aggregateRoute.reRouteIsCaseSensitive = route.reRouteIsCaseSensitive + aggregateRoute.reRouteKeys = route.reRouteKeys + aggregateRoute.routeId = route.reRouteId + aggregateRoute.upstreamHost = route.upstreamHost + aggregateRoute.upstreamHttpMethod = route.upstreamHttpMethod + aggregateRoute.upstreamPathTemplate = route.upstreamPathTemplate + return aggregateRoute + } +} + +export class AggregateReRouteGetByPaged extends PagedAndSortedResultRequestDto { + appId = '' + filter = '' + + public static empty() { + return new AggregateReRouteGetByPaged() + } +} diff --git a/vueJs/src/lang/zh.ts b/vueJs/src/lang/zh.ts index 3474d241d..6896f5d09 100644 --- a/vueJs/src/lang/zh.ts +++ b/vueJs/src/lang/zh.ts @@ -72,6 +72,7 @@ export default { group: '路由分组', global: '全局配置', route: '路由配置', + aggregateRoute: '聚合路由', identityServer: '身份认证服务器', clients: '客户端管理', apiresources: 'Api资源管理', @@ -343,6 +344,7 @@ export default { appIdHasRequired: '应用标识不能为空!', upstreamPathTemplate: '上游请求路径', upstreamHttpMethod: '上游请求方式', + upstreamHost: '上游主机地址', downstreamHostAndPorts: '下游请求地址', downstreamPathTemplate: '下游请求路径', serviceName: '服务名称', @@ -369,7 +371,20 @@ export default { ipAllowedList: 'Ip白名单', ipBlockedList: 'Ip黑名单', authenticationProviderKey: '身份认证程序', - allowedScopes: '允许认证范围' + allowedScopes: '允许认证范围', + createAggregateRoute: '新建聚合', + aggregateRouteName: '聚合名称', + reRouteKeys: '路由标识列表', + aggregateOptions: '聚合选项', + updateAggregateRoute: '编辑聚合', + updateAggregateRouteByName: '编辑聚合 {name}', + deleteAggregateRoute: '删除聚合', + deleteAggregateRouteByName: '删除聚合 {name}', + deleteAggregateRouteSuccess: '聚合路由 {name} 已删除!', + createAggregateRouteKey: '新建聚合参数', + aggregateRouteKey: '聚合路由标识', + aggregateParameter: '聚合参数', + aggregateJsonPath: 'Json路径' }, identityServer: { otherOpera: '更多操作', diff --git a/vueJs/src/router/index.ts b/vueJs/src/router/index.ts index 916b00ea8..bd6001224 100644 --- a/vueJs/src/router/index.ts +++ b/vueJs/src/router/index.ts @@ -290,6 +290,16 @@ export const asyncRoutes: RouteConfig[] = [ icon: 'route', roles: ['ApiGateway.Route'] } + }, + { + path: 'aggregateRoute', + component: () => import('@/views/admin/apigateway/aggregateRoute.vue'), + name: 'aggregateRoute', + meta: { + title: 'aggregateRoute', + icon: 'aggregateRoute', + roles: ['ApiGateway.AggregateRoute'] + } } ] }, diff --git a/vueJs/src/views/admin/apigateway/aggregateRoute.vue b/vueJs/src/views/admin/apigateway/aggregateRoute.vue index e69de29bb..4a2febdc2 100644 --- a/vueJs/src/views/admin/apigateway/aggregateRoute.vue +++ b/vueJs/src/views/admin/apigateway/aggregateRoute.vue @@ -0,0 +1,295 @@ + + + diff --git a/vueJs/src/views/admin/apigateway/components/AggregateRouteCreateOrEditForm.vue b/vueJs/src/views/admin/apigateway/components/AggregateRouteCreateOrEditForm.vue new file mode 100644 index 000000000..00e2da463 --- /dev/null +++ b/vueJs/src/views/admin/apigateway/components/AggregateRouteCreateOrEditForm.vue @@ -0,0 +1,197 @@ + + + diff --git a/vueJs/src/views/admin/apigateway/components/RouteCreateOrEditForm.vue b/vueJs/src/views/admin/apigateway/components/RouteCreateOrEditForm.vue index c52e9bec0..baad2603c 100644 --- a/vueJs/src/views/admin/apigateway/components/RouteCreateOrEditForm.vue +++ b/vueJs/src/views/admin/apigateway/components/RouteCreateOrEditForm.vue @@ -34,7 +34,6 @@