<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 Yarp 做網(wǎng)關(guān)+.NET 6.0 + Swagger

          共 53387字,需瀏覽 107分鐘

           ·

          2022-07-13 19:12


          資料


          GitHub: https://github.com/microsoft/reverse-proxy

          YARP 文檔

          https://microsoft.github.io/reverse-proxy/articles/getting-started.html

          主動(dòng)和被動(dòng)健康檢查 

          https://microsoft.github.io/reverse-proxy/articles/dests-health-checks.html#active-health-check

          gRpc:https://microsoft.github.io/reverse-proxy/articles/grpc.html

          實(shí)戰(zhàn)項(xiàng)目概覽

          Yarp Gateway 示意圖

          共享類庫(kù)

          創(chuàng)建一個(gè) .Net6.0 的類庫(kù),項(xiàng)目名稱:Artisan.Shared.Hosting.AspNetCore, 其它項(xiàng)目公用方法放在這個(gè)項(xiàng)目。

          Serilog 日志

          需要的包:

              <PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
              <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
              <PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
              <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />

          代碼清單:Artisan.Shared.Hosting.AspNetCore/SerilogConfigurationHelper.cs

          using Serilog;
          using Serilog.Events;

          namespace Artisan.Shared.Hosting.AspNetCore;

          public static class SerilogConfigurationHelper
          {
              public static void Configure(string applicationName)
              {
                  Log.Logger = new LoggerConfiguration()
          #if DEBUG
                      .MinimumLevel.Debug()
          #else
                      .MinimumLevel.Information()
          #endif
                      .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                      .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
                      .Enrich.FromLogContext()
                      .Enrich.WithProperty("Application"$"{applicationName}")
                      .WriteTo.Async(c => c.File($"{AppDomain.CurrentDomain.BaseDirectory}/Logs/logs.txt"))
                      .WriteTo.Async(c => c.Console())
                      .CreateLogger();
              }
          }

          創(chuàng)建服務(wù)


          IdentityService

          創(chuàng)建一個(gè)【AspNetCore Web Api】項(xiàng)目,項(xiàng)目名稱為:IdentityService

          Program

          代碼清單:IdentityService/Program.cs

          using Artisan.Shared.Hosting.AspNetCore;
          using Microsoft.OpenApi.Models;
          using Serilog;

          namespace IdentityService;

          public class Program
          {
              public static int Main(string[] args)
              {
                  var assemblyName = typeof(Program).Assembly.GetName().Name;

                  SerilogConfigurationHelper.Configure(assemblyName);

                  try
                  {
                      Log.Information($"Starting {assemblyName}.");

                      var builder = WebApplication.CreateBuilder(args);
                      builder.Host
                          .UseSerilog();

                      builder.Services.AddControllers(); //Web MVC
                      builder.Services.AddSwaggerGen(options =>
                      {
                          options.SwaggerDoc("v1"new OpenApiInfo { Title = "Identity Service", Version = "v1" });
                          options.DocInclusionPredicate((docName, description) => true);
                          options.CustomSchemaIds(type => type.FullName);
                      });

                      var app = builder.Build();
                      if (app.Environment.IsDevelopment())
                      {
                          app.UseDeveloperExceptionPage();
                      }
                      app.UseRouting();
                      app.UseSwagger();
                      app.UseSwaggerUI();
                      app.UseEndpoints(endpoints =>
                      {
                          endpoints.MapControllers(); //Web MVC
                      });

                      app.Run();

                      return 0;
                  }
                  catch (Exception ex)
                  {
                      Log.Fatal(ex, $"{assemblyName} terminated unexpectedly!");
                      return 1;
                  }
                  finally
                  {
                      Log.CloseAndFlush();
                  }
              }
          }

          其中:

           SerilogConfigurationHelper.Configure(assemblyName);

          是配置 Serilog 日志:引用上面創(chuàng)建的共享項(xiàng)目:【Artisan.Shared.Hosting.AspNetCore】

          User 實(shí)體

          代碼清單:IdentityService/Models/User.cs

              public class User
              {
                  public int Id { getset; }
                  public string Name { getset; }
              }

          UserController

          代碼清單:IdentityService/Controlles/UserController.cs

          using Microsoft.AspNetCore.Mvc;
          using IdentityService.Models;
          using System.Threading.Tasks;

          namespace IdentityService.Controllers
          {
              [ApiController]
              [Route("/api/identity/users")]
              public class UserController : Controller
              {
                  private readonly ILogger<UserController> _logger;

                  private static List<User> Users = new List<User>()
                  {
                      new User(){ Id = 1, Name = "Jack"},
                      new User(){ Id = 2, Name = "Tom"},
                      new User(){ Id = 3, Name = "Franck"},
                      new User(){ Id = 4, Name = "Tony"},
                  };

                  public UserController(ILogger<UserController> logger)
                  {
                      _logger = logger;
                  }

                  [HttpGet]
                  public async Task<List<User>>  GetAllAsync()
                  {
                      return await Task.Run(() => 
                      { 
                          return Users; 
                      });
                  }


                  [HttpGet]
                  [Route("{id}")]
                  public async Task<User> GetAsync(int id)
                  {
                      return await Task.Run(() =>
                      {
                          var entity = Users.FirstOrDefault(p => p.Id == id);
                          if (entity == null)
                          {
                              throw new Exception($"未找到用戶:{id}");
                          }
                          return entity;
                      });
                  }

                  [HttpPost]
                  public async Task<User> CreateAsync(User user)
                  {
                      return await Task.Run(() =>
                      {
                          Users.Add(user);
                          return user;
                      });
                  }

                  [HttpPut]
                  [Route("{id}")]
                  public async Task<User> UpdateAsync(int id, User user)
                  {
                      return await Task.Run(() =>
                      {
                          var entity = Users.FirstOrDefault(p => p.Id == id);
                          if(entity == null)
                          {
                              throw new Exception($"未找到用戶:{id}");
                          }
                          entity.Name = user.Name;
                          return entity;
                      });
                  }

                  [HttpDelete]
                  [Route("{id}")]
                  public async Task<User> DeleteAsync(int id)
                  {
                      return await Task.Run(() =>
                      {
                          var entity = Users.FirstOrDefault(p => p.Id == id);
                          if (entity == null)
                          {
                              throw new Exception($"未找到用戶:{id}");
                          }
                          Users.Remove(entity);

                          return entity;
                      });
                  }
              }
          }

          OrderService

          創(chuàng)建一個(gè)【AspNetCore Web Api】項(xiàng)目,項(xiàng)目名稱為:OrderService

          Program

          代碼清單:OrderService/Program.cs

          using Artisan.Shared.Hosting.AspNetCore;
          using Microsoft.OpenApi.Models;
          using Serilog;

          namespace OrderService;
          public class Program
          {
              public static int Main(string[] args)
              {
                  var assemblyName = typeof(Program).Assembly.GetName().Name;

                  SerilogConfigurationHelper.Configure(assemblyName);

                  try
                  {
                      Log.Information($"Starting {assemblyName}.");

                      var builder = WebApplication.CreateBuilder(args);
                      builder.Host
                          .UseSerilog();

                      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);
                      });

                      var app = builder.Build();
                      if (app.Environment.IsDevelopment())
                      {
                          app.UseDeveloperExceptionPage();
                      }
                      app.UseRouting();
                      app.UseSwagger();
                      app.UseSwaggerUI();
                      app.UseEndpoints(endpoints =>
                      {
                          endpoints.MapControllers(); //Web MVC
                      });

                      app.Run();

                      return 0;
                  }
                  catch (Exception ex)
                  {
                      Log.Fatal(ex, $"{assemblyName} terminated unexpectedly!");
                      return 1;
                  }
                  finally
                  {
                      Log.CloseAndFlush();
                  }
              }
          }

          Order 實(shí)體

          代碼清單:OrderService/Models/Order.cs

              public class Order
              {
                  public string Id { getset; }
                  public string Name { getset; }
              }

          OrderController

          代碼清單:OrderService/Controlles/OrderController.cs

          using Microsoft.AspNetCore.Mvc;
          using OrderService.Models;
          using System.Diagnostics;

          namespace OrderService.Controllers
          {
              [ApiController]
              [Route("/api/ordering/orders")]
              public class OrderController : Controller
              {
                  private readonly ILogger<OrderController> _logger;

                  private static List<Order> Orders = new List<Order>()
                  {
                      new Order(){ Id = "1", Name = "Order #1"},
                      new Order(){ Id = "2", Name = "Order #2"},
                      new Order(){ Id = "3", Name = "Order #3"},
                      new Order(){ Id = "4", Name = "Order #4"},
                  };

                  public OrderController(ILogger<OrderController> logger)
                  {
                      _logger = logger;
                  }

                  [HttpGet]
                  public async Task<List<Order>> GetAllAsync()
                  {
                      return await Task.Run(() =>
                      {
                          return Orders;
                      });
                  }


                  [HttpGet]
                  [Route("{id}")]
                  public async Task<Order> GetAsync(string id)
                  {
                      return await Task.Run(() =>
                      {
                          var entity = Orders.FirstOrDefault(p => p.Id == id);
                          if (entity == null)
                          {
                              throw new Exception($"未找到訂單:{id}");
                          }
                          return entity;
                      });
                  }

                  [HttpPost]
                  public async Task<Order> CreateAsync(Order order)
                  {
                      return await Task.Run(() =>
                      {
                          Orders.Add(order);
                          return order;
                      });
                  }

                  [HttpPut]
                  [Route("{id}")]
                  public async Task<Order> UpdateAsync(string id, Order Order)
                  {
                      return await Task.Run(() =>
                      {
                          var entity = Orders.FirstOrDefault(p => p.Id == id);
                          if (entity == null)
                          {
                              throw new Exception($"未找到訂單:{id}");
                          }
                          entity.Name = Order.Name;
                          return entity;
                      });
                  }

                  [HttpDelete]
                  [Route("{id}")]
                  public async Task<Order> DeleteAsync(string id)
                  {
                      return await Task.Run(() =>
                      {
                          var entity = Orders.FirstOrDefault(p => p.Id == id);
                          if (entity == null)
                          {
                              throw new Exception($"未找到訂單:{id}");
                          }
                          Orders.Remove(entity);

                          return entity;
                      });
                  }
              }
          }

          創(chuàng)建網(wǎng)關(guān)

          創(chuàng)建一個(gè)【AspNetCore 空】項(xiàng)目,項(xiàng)目名稱為:YarpGateway

          引用包

           <PackageReference Include="Yarp.ReverseProxy" Version="1.1.0" />

          添加 Yarp

          代碼清單:YarpGateway/Program.cs

          using Artisan.Shared.Hosting.AspNetCore;
          using Serilog;
          using YarpGateway.Extensions;

          namespace YarpGateway;

          public class Program
          {
              public static  int Main(string[] args)
              {
                  var assemblyName = typeof(Program).Assembly.GetName().Name;

                  SerilogConfigurationHelper.Configure(assemblyName);

                  try
                  {
                      Log.Information($"Starting {assemblyName}.");

                      var builder = WebApplication.CreateBuilder(args);
                      builder.Host
                          .UseSerilog()
                          .AddYarpJson(); // 添加Yarp的配置文件

                      // 添加Yarp反向代理ReverseProxy
                      builder.Services.AddReverseProxy()
                          .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

                      var app = builder.Build();

                      app.UseRouting();
                      app.UseEndpoints(endpoints =>
                      {
                          // 添加Yarp終端Endpoints
                          endpoints.MapReverseProxy();
                      });

                      app.Run();

                      return 0;
                  }
                  catch (Exception ex)
                  {
                      Log.Fatal(ex, $"{assemblyName} terminated unexpectedly!");
                      return 1;
                  }
                  finally
                  {
                      Log.CloseAndFlush();
                  }
              }
          }

          其中:

          方法AddYarpJson() 是為了把 Yarp 的有關(guān)配置從appsetting.json獨(dú)立處理,避免配置文件很長(zhǎng)很長(zhǎng),其代碼如下:

          代碼清單:YarpGateway/Extensions/GatewayHostBuilderExtensions.cs

          namespace YarpGateway.Extensions;

          public static class GatewayHostBuilderExtensions

              public const string AppYarpJsonPath = "yarp.json";

              public static IHostBuilder AddYarpJson(
                  this IHostBuilder hostBuilder,
                  bool optional = true,
                  bool reloadOnChange = true,
                  string path = AppYarpJsonPath
          )

              {
                  return hostBuilder.ConfigureAppConfiguration((_, builder) =>
                  {
                      builder.AddJsonFile(
                              path: AppYarpJsonPath,
                              optional: optional,
                              reloadOnChange: reloadOnChange
                          )
                          .AddEnvironmentVariables();
                  });
              }
          }

          其中:

          reloadOnChange = true 保證配置文件修改時(shí), Yarp 能重新讀取配置文件。

          添加 Yarp配置文件 : yarp.json

          記得保證文件的屬性:

          • 復(fù)制到輸出目錄:如果內(nèi)容較新則復(fù)制
          • 生成操作:內(nèi)容

          代碼清單:YarpGateway/yarp.json

          {
            "ReverseProxy": {
              "Routes": {
                "Identity Service": {
                  "ClusterId""identityCluster",
                  "Match": {
                    "Path""/api/identity/{**everything}"
                  }
                },
                "Ordering Service": {
                  "ClusterId""orderingCluster",
                  "Match": {
                    "Path""/api/ordering/{**everything}"
                  }
                }
              },
              "Clusters": {
                "identityCluster": {
                  "Destinations": {
                    "destination1": {
                      "Address""http://localhost:7711"
                    }
                  }
                },
                "orderingCluster": {
                  "Destinations": {
                    "destination1": {
                      "Address""http://localhost:7721"
                    }
                    "destination2": {
                      "Address""http://localhost:7722"
                    }
                  }
                }
              }
            }
          }

          運(yùn)行

          Yarp Gateway 示意圖:

          啟動(dòng)網(wǎng)關(guān)

          在項(xiàng)目的bin/net6.0目錄下打開(kāi) CMD,執(zhí)行如下命令啟動(dòng)網(wǎng)關(guān):

          dotnet YarpGateway.dll --urls "http://localhost:7700"

          監(jiān)聽(tīng)端口:7700

          IdentityService

          在項(xiàng)目的bin/net6.0目錄下打開(kāi) CMD,執(zhí)行如下命令啟動(dòng) Web API 服務(wù):

          dotnet IdentityService.dll --urls "http://localhost:7711"

          監(jiān)聽(tīng)端口:7711

          OrderService

          開(kāi)啟兩個(gè) OrderServcie 的進(jìn)程,

          bin/net6.0目錄下打開(kāi) CMD,執(zhí)行如下命令啟動(dòng) Web API 服務(wù):

          第一個(gè)監(jiān)聽(tīng)端口:7721

          dotnet OrderService.dll --urls "http://localhost:7721"

          第二個(gè)監(jiān)聽(tīng)端口:7722

          dotnet OrderService.dll --urls "http://localhost:7722"

          測(cè)試


          路由功能

          打開(kāi) PostMan,創(chuàng)建調(diào)用服務(wù)的各種請(qǐng)求

          IdentityService

          創(chuàng)建 GET 請(qǐng)求調(diào)用網(wǎng)關(guān):http://localhost:7700/api/identity/users

          請(qǐng)求會(huì)被轉(zhuǎn)發(fā)到 IdentityService的集群節(jié)點(diǎn):http://localhost:7711/api/identity/users

          OrderService

          創(chuàng)建 GET 請(qǐng)求調(diào)用網(wǎng)關(guān):http://localhost:7700/api/ordering/orders

          請(qǐng)求會(huì)被轉(zhuǎn)發(fā)到 OrderService 的集群中如下某個(gè)節(jié)點(diǎn)中的一個(gè):

          1. http://localhost:7721/api/ordering/orders
          2. http://localhost:7722/api/ordering/orders

          支持請(qǐng)求類型

          Tips:

          由于是兩個(gè)服務(wù),每個(gè)服務(wù)的進(jìn)程都是獨(dú)立的,數(shù)據(jù)也是獨(dú)立,數(shù)據(jù)并沒(méi)有共享,故測(cè)試結(jié)果可能不是你所預(yù)期的,比如:

          第一步:增加數(shù)據(jù),這次是由第一個(gè)服務(wù)處理的;

          第二步:查詢數(shù)據(jù),如果這次查詢是由第二個(gè)服務(wù)器處理的話,就會(huì)找不到剛才新增的數(shù)據(jù)。

          當(dāng)然在實(shí)際開(kāi)發(fā)中,我們的數(shù)據(jù)都是從同一個(gè)數(shù)據(jù)庫(kù)中讀取,不會(huì)出現(xiàn)數(shù)據(jù)不一致的情況。

          HTTP 1.0 / 2.0

          創(chuàng)建 GET 請(qǐng)求:http://localhost:7700/api/ordering/orders/1

          創(chuàng)建 POST 請(qǐng)求:http://localhost:7700/api/ordering/orders 

          參數(shù):

          {
              "id":"10",
              "name":"Order #100"
          }

          創(chuàng)建 PUT 請(qǐng)求:http://localhost:7700/api/ordering/orders/10 

          參數(shù):

          {
          "id":"10",
          "name":"Order #100-1"
          }

          創(chuàng)建 DELETE 請(qǐng)求:http://localhost:7700/api/ordering/orders/10

          結(jié)論

          上述4種 HTTP 請(qǐng)求都支持。

          gRpc

          待測(cè)試...

          結(jié)論

          支持 gRpc

          新增集群服務(wù)節(jié)點(diǎn)

          Yarp 支持動(dòng)態(tài)添加服務(wù)集群服務(wù)節(jié)點(diǎn),只要在配置文件 yarp.json, 添加新的服務(wù)配置,Yarp會(huì)自動(dòng)加載新的服務(wù)節(jié)點(diǎn):

          代碼清單:yarp.json

          {
            "ReverseProxy": {
              "Routes": {
                "Identity Service": {
                  "ClusterId""identityCluster",
                  "Match": {
                    "Path""/api/identity/{**everything}"
                  }
                },
                 ...
              },
               "Clusters": {
                    "orderingCluster": {
                      "Destinations": {
                        "destination1": {
                          "Address""http://localhost:7721"
                        },
              +          "destination2": {
              +            "Address""http://localhost:7722"
              +          }
                      }
                }
              }
            }
          }

          添加上述配置后,會(huì)看到如下日志信息:

          14:51:11 DBG] Destination 'destination2' has been added.
          [14:51:11 DBG] Existing client reused for cluster 'orderingCluster'.

          結(jié)論

          Yarp 會(huì)重新加載配置,使得新增的集群新服務(wù)節(jié)點(diǎn)生效。

          刪除集群服務(wù)節(jié)點(diǎn)

          刪除集群下的某個(gè)服務(wù)節(jié)點(diǎn)

          -          "destination2": {
          - "Address": "http://localhost:7722"
          - }

          Yarp 會(huì)重新加載配置,該集群服務(wù)節(jié)點(diǎn)被刪除。

          [14:41:26 DBG] Destination 'destination2' has been removed.
          [14:41:26 DBG] Existing client reused for cluster 'orderingCluster'.

          結(jié)論

          Yarp 會(huì)重新加載配置,使得被刪除的集群服務(wù)節(jié)點(diǎn)配置失效。

          某集群節(jié)點(diǎn)因故障離線

          把監(jiān)聽(tīng)7722端口的服務(wù)終止,請(qǐng)求還是會(huì)發(fā)送到這個(gè)端口程序上!?。?/p>

          結(jié)論

          Yarp 默認(rèn)不會(huì)做健康檢查

          如何通過(guò) YarpGateway 訪問(wèn)內(nèi)部服務(wù)的Swagger呢?

          問(wèn)題:無(wú)法訪問(wèn)內(nèi)部服務(wù) Swagger

          外部訪問(wèn) IdentityService 和 OrderService 是通過(guò) 網(wǎng)關(guān):YarpGateway 訪問(wèn)的,使用者這個(gè)并不知道這個(gè)兩個(gè)服務(wù)的具體地址,也就是不知道如何訪問(wèn)它們的 Swagger,那么:

          如何通過(guò) YarpGateway 訪問(wèn)這兩個(gè)服務(wù)的Swagger呢?

          實(shí)現(xiàn)原理

          使用網(wǎng)關(guān)內(nèi)部服務(wù)的 Swagger 信息,其地址為:

          http://ip:port/swagger/v1/swagger.json

          例如,OrderService 服務(wù)的 Swagger 信息為:

          http://localhost:7721/swagger/v1/swagger.json

          在網(wǎng)關(guān)中使用內(nèi)部服務(wù)的 Swagger 終點(diǎn),再注冊(cè) Swagger 終點(diǎn)。

          訪問(wèn) OrderService 服務(wù)的 Swagger 信息地址:http://localhost:7711/swagger/v1/swagger.json

          返回如下信息:(只列舉部分?jǐn)?shù)據(jù))

          {
            "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"
                            }
                          }
                        }
                      }
                    }
                  }
                },
                  
               .....

          內(nèi)部服務(wù)支持跨域

          網(wǎng)關(guān)要請(qǐng)求內(nèi)部服務(wù)的Swagger 信息,這是跨域請(qǐng)求,所以要求兩個(gè)服務(wù)支持對(duì)網(wǎng)關(guān)的跨域請(qǐng)求。

          IdentityServiceOrderService 項(xiàng)目中都做如下修改:

          添加跨域配置

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

          {
            "App": {
              "CorsOrigins""http://localhost:7700"      // 網(wǎng)關(guān)地址,支持網(wǎng)關(guān)的Yarp gatewary跨域請(qǐng)求
            }
          }

          其中,這個(gè)地址**http://localhost:7700** 就是網(wǎng)關(guān)的地址。

          支持跨域

          修改 Program.cs文件:

          1、代碼清單:IdentityService/Program.cs
          2、代碼清單: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();
          .....

          網(wǎng)關(guān)添加 Swagger

          在網(wǎng)關(guān)項(xiàng)目【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);
                      });

                      ......
                      // 添加內(nèi)部服務(wù)的Swagger終點(diǎn)
                      app.UseSwaggerUIWithYarp();
                      //訪問(wèn)網(wǎng)關(guān)地址,自動(dòng)跳轉(zhuǎn)到 /swagger 的首頁(yè)
                      app.UseRewriter(new RewriteOptions() 
                          // Regex for "", "/" and "" (whitespace)
                          .AddRedirect("^(|\\|\\s+)$""/swagger"));

                      app.UseRouting();

          其中,調(diào)用方法 app.UseSwaggerUIWithYarp(); 的目的是:

          添加內(nèi)部服務(wù)的Swagger終點(diǎn),其代碼如下:

          代碼清單: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;
              }
          }

          關(guān)鍵代碼:

          options.SwaggerEndpoint($"{clusterGroup.Value.Address}/swagger/v1/swagger.json"$"{routeConfig.RouteId} API");

          通過(guò) IProxyConfigProvider 得到內(nèi)部服務(wù)的信息,如下圖所示:

          然后,拼接出內(nèi)部服務(wù)的 Swagger 信息地址,

          $"{clusterGroup.Value.Address}/swagger/v1/swagger.json"

          最終得到兩個(gè)服務(wù)的Swagger信息地址:

          • IdentityServer 的 Swagger 信息地址:
          http://localhost:7711/swagger/v1/swagger.json
          • OrderService 的 Swagger 信息地址:
          http://localhost:7721/swagger/v1/swagger.json

          最后,根據(jù)信息添加Swagger終點(diǎn):

          options.SwaggerEndpoint(
                  $"{clusterGroup.Value.Address}/swagger/v1/swagger.json"
                  $"{routeConfig.RouteId} API"
          );

          其中,

          routeConfig.RouteId: Identity Service 或 Ordering Service

          訪問(wèn)網(wǎng)關(guān) Swagger

          訪問(wèn)網(wǎng)關(guān)地址:http://localhost:7700

          自動(dòng)跳轉(zhuǎn)到其 Swagger首頁(yè):http://localhost:7700/swagger/index.html

          右上角有個(gè)下拉框,可以選擇不同的服務(wù)的Swagger,這里切換到 OrderService 的Swagger,如下圖所示:

          在網(wǎng)關(guān) Swagger 調(diào)用服務(wù)接口

          可以在網(wǎng)關(guān) Swagger 調(diào)用內(nèi)部服務(wù)接口,如下圖所示:

          返回


          轉(zhuǎn)自:easy5

          鏈接:cnblogs.com/easy5weikai/p/16314830.html

          瀏覽 77
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  www.性爱AV | 日本成人大香蕉视频在线观看 | 精品乱码人妻 | 免费观看一区二区三区四区五区 | 乱伦福利片 |