使用 Yarp 做网关(二) : 网关 Swagger

2022/5/26 23:24:10

本文主要是介绍使用 Yarp 做网关(二) : 网关 Swagger,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Yarp & Swagger

目录
  • Yarp & Swagger
    • 问题:无法访问内部服务 Swagger
    • 实现原理
    • 内部服务支持跨域
      • 添加跨域配置
      • 支持跨域
    • 网关添加 Swagger
    • 访问网关 Swagger
    • 在网关 Swagger 调用服务接口

接着上一节 使用 Yarp 做网关 (一),
完成上一节的练习后,还遗留了一个问题:
如何通过 YarpGateway 访问内部服务的Swagger呢?

问题:无法访问内部服务 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 信息,这是跨域请求,所以要求两个服务支持对网关的跨域请求。

IdentityServiceOrderService 项目中都做如下修改:

添加跨域配置

appsettins.json 文件中添加跨域配置:

{
  "App": {
    "CorsOrigins": "http://localhost:7700"      // 网关地址,支持网关的Yarp gatewary跨域请求
  }
}

其中,这个地址http://localhost:7700 就是网关的地址

支持跨域

修改 Program.cs文件:
代码清单:IdentityService/Program.cs

代码清单:OrderService/Program.cs

......
            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 = "Order Service", Version = "v1" 
                });
                options.DocInclusionPredicate((docName, description) => true);
                options.CustomSchemaIds(type => type.FullName);
            });

            ......
            // 添加内部服务的Swagger终点
            app.UseSwaggerUIWithYarp();
            //访问网关地址,自动跳转到 /swagger 的首页
            app.UseRewriter(new RewriteOptions() 
                // Regex for "", "/" and "" (whitespace)
                .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

自动跳转到地址:http://localhost:7700/swagger/index.html

右上角有个下拉框,可以选择不同的服务的Swagger,这里切换到 OrderService 的Swagger,如下图所示:

在网关 Swagger 调用服务接口

可以在网关 Swagger 调用内部服务接口,如下图所示:

返回:






这篇关于使用 Yarp 做网关(二) : 网关 Swagger的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程