1. 程式人生 > 其它 >使用 Yarp 做閘道器(二) : 閘道器 Swagger

使用 Yarp 做閘道器(二) : 閘道器 Swagger

Yarp & 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 終點。

訪問: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檔案

......
            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 呼叫內部服務介面,如下圖所示:

返回: