使用 Yarp 做网关(二) : 网关 Swagger
Yarp & Swagger
源码
https://gitee.com/Artisan-k/yarn-samlpes
问题:无法访问内部服务 Swagger
接着上一节 使用 Yarp 做网关 (一),
完成上一节的练习后,还遗留了一个问题:
如何通过 YarpGateway 访问内部服务的Swagger呢?
外部访问 IdentityService 和 OrderService 是通过网关:YarpGateway 访问的,使用者并不知道这个两个服务的具体地址,也自然不知道如何访问它们的 Swagger,那么:
如何在通过访问网关 YarpGateway 来访问这两个服务的 Swagger 呢?,这就是本教程的要探讨的内容。
实现原理
使用网关内部服务的 Swagger 信息,其地址为:
http://ip:port/swagger/v1/swagger.json
例如,OrderService 服务的 Swagger 信息为:
http://localhost:7721/swagger/v1/swagger.json
在网关中使用内部服务的 Swagger 终点,再注册 Swagger 终点。
访问 OrderService 服务的 Swagger 信息地址:http://localhost:7711/swagger/v1/swagger.json
返回如下信息:(只列举部分数据)
{ "openapi": "3.0.1", "info": { "title": "Identity Service", "version": "v1" }, "paths": { "/api/identity/users": { "get": { "tags": [ "User" ], "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/IdentityService.Models.User" } } }, "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/IdentityService.Models.User" } } }, "text/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/IdentityService.Models.User" } } } } } } }, .....
内部服务支持跨域
网关要请求内部服务的Swagger 信息,这是跨域请求,所以要求两个服务支持对网关的跨域请求。
在IdentityService 和 OrderService 项目中都做如下修改:
添加跨域配置
在 appsettins.json
文件中添加跨域配置:
{
"App": {
"CorsOrigins": "http://localhost:7700" // 网关地址,支持网关的Yarp gatewary跨域请求
}
}
其中,这个地址http://localhost:7700 就是网关的地址。
支持跨域
修改 Program.cs
文件:
-
代码清单:IdentityService/Program.cs
-
代码清单:OrderService/Program.cs
......
IConfiguration configuration = builder.Configuration;
+ builder.Services.AddCors(options =>
+ {
+ options.AddDefaultPolicy(builder =>
+ {
+ builder
+ .WithOrigins(
+ configuration["App:CorsOrigins"]
+ .Split(",", StringSplitOptions.RemoveEmptyEntries)
+ .ToArray()
+ )
+ .SetIsOriginAllowedToAllowWildcardSubdomains()
+ .AllowAnyHeader()
+ .AllowAnyMethod()
+ .AllowCredentials();
+ });
+ });
......
app.UseRouting();
+ app.UseCors(); // 添加跨域支持
app.UseSwagger();
app.UseSwaggerUI();
.....
网关添加 Swagger
在网关项目【YarpGateway】中做如下修改:
代码清单:YarpGateway/Program.cs
builder.Services.AddControllers(); //Web MVC
......
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Gateway", Version = "v1"
});
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
......
+ // 添加内部服务的Swagger终点
+ app.UseSwaggerUIWithYarp();
+ //访问网关地址,自动跳转到 /swagger 的首页, Regex for "", "/" and "" (whitespace)
+ app.UseRewriter(new RewriteOptions()
+ .AddRedirect("^(|\\|\\s+)$", "/swagger"));
app.UseRouting();
其中,调用方法 app.UseSwaggerUIWithYarp(); 的目的是:添加内部服务的Swagger终点,其代码如下:
代码清单:YarpGateway/Extensions/YarpSwaggerUIBuilderExtensions.cs
using Yarp.ReverseProxy.Configuration;
namespace YarpGateway.Extensions;
public static class YarpSwaggerUIBuilderExtensions
{
public static IApplicationBuilder UseSwaggerUIWithYarp(this IApplicationBuilder app)
{
var serviceProvider = app.ApplicationServices;
app.UseSwagger();
app.UseSwaggerUI(options =>
{
var configuration = serviceProvider.GetRequiredService<IConfiguration>();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
var proxyConfigProvider = serviceProvider.GetRequiredService<IProxyConfigProvider>();
var yarpConfig = proxyConfigProvider.GetConfig();
var routedClusters = yarpConfig.Clusters
.SelectMany(t => t.Destinations,
(clusterId, destination) => new { clusterId.ClusterId, destination.Value });
var groupedClusters = routedClusters
.GroupBy(q => q.Value.Address)
.Select(t => t.First())
.Distinct()
.ToList();
foreach (var clusterGroup in groupedClusters)
{
var routeConfig = yarpConfig.Routes.FirstOrDefault(q =>
q.ClusterId == clusterGroup.ClusterId);
if (routeConfig == null)
{
logger.LogWarning($"Swagger UI: Couldn't find route configuration for {clusterGroup.ClusterId}...");
continue;
}
options.SwaggerEndpoint($"{clusterGroup.Value.Address}/swagger/v1/swagger.json", $"{routeConfig.RouteId} API");
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
}
});
return app;
}
}
关键代码:
options.SwaggerEndpoint($"{clusterGroup.Value.Address}/swagger/v1/swagger.json", $"{routeConfig.RouteId} API");
通过 IProxyConfigProvider 得到内部服务的信息,如下图所示:
然后,拼接出内部服务的 Swagger 信息地址,
$"{clusterGroup.Value.Address}/swagger/v1/swagger.json"
最终得到两个服务的Swagger信息地址:
- IdentityServer 的 Swagger 信息地址:
http://localhost:7711/swagger/v1/swagger.json
- OrderService 的 Swagger 信息地址:
http://localhost:7721/swagger/v1/swagger.json
最后,根据信息添加Swagger终点:
options.SwaggerEndpoint(
$"{clusterGroup.Value.Address}/swagger/v1/swagger.json",
$"{routeConfig.RouteId} API"
);
其中,
routeConfig.RouteId
: Identity Service 或 Ordering Service
访问网关 Swagger
访问网关地址:http://localhost:7700
自动跳转到其 Swagger首页:http://localhost:7700/swagger/index.html
右上角有个下拉框,可以选择不同的服务的Swagger,这里切换到 OrderService 的Swagger,如下图所示:
在网关 Swagger 调用服务接口
可以在网关 Swagger 调用内部服务接口,如下图所示:
返回:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10亿数据,如何做迁移?
· 推荐几款开源且免费的 .NET MAUI 组件库
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 易语言 —— 开山篇
· Trae初体验