使用 Ocelot 实现 API 网关

小窍门

此内容摘自电子书《适用于容器化 .NET 应用程序的 .NET 微服务体系结构》,可在 .NET Docs 上找到,或下载免费的 PDF 以便脱机阅读。

适用于容器化 .NET 应用程序的 .NET 微服务体系结构电子书封面缩略图。

重要

引用微服务应用程序 eShopOnContainers 目前使用 Envoy 提供的功能来实现 API 网关,而不是之前引用的 Ocelot。 在 eShopOnContainers 中,由于实现的 gRPC 服务间新通信需要 WebSocket 协议,而 Envoy 内置支持该协议,我们因此做出了这种设计选择。 但是,我们在指南中保留了本部分,因此可以将 Ocelot 视为适用于生产级方案的简单、支持和轻型 API 网关。 此外,最新的 Ocelot 版本对其 JSON Schema 作了重大变更。 请考虑使用 Ocelot < v16.0.0,或使用密钥路由而不是 ReRoutes。

构建和设计 API 网关

以下体系结构图显示了如何在 eShopOnContainers 中使用 Ocelot 实现 API 网关。

eShopOnContainers 体系结构图表。

图 6-28. 使用 API 网关的 eShopOnContainers 体系结构

该图显示了如何将整个应用程序部署到具有“Docker for Windows”或“Docker for Mac”的单个 Docker 主机或开发电脑上。 但是,部署到任何业务流程协调程序将类似,但关系图中的任何容器都可以在业务流程协调程序中横向扩展。

此外,应从业务流程协调程序卸载基础结构资产(如数据库、缓存和消息代理),并将其部署到基础结构的高可用系统中,例如 Azure SQL 数据库、Azure Cosmos DB、Azure Redis、Azure 服务总线或任何本地 HA 群集解决方案。

如图中所示,拥有多个 API 网关允许多个开发团队在开发和部署微服务及其自己的相关 API 网关时进行自治(在本例中,营销功能与购物功能)。

如果你有一个单一的整体 API 网关,这意味着多个开发团队需要更新相同的一个点,这可能会将所有微服务与应用程序的单个部分耦合在一起。

深入设计时,有时细粒度的 API 网关也可能局限于单个业务微服务,这取决于所选的架构。 让 API 网关的边界由业务或域决定,可帮助你获得更好的设计。

例如,API 网关层中的精细粒度对于基于微服务的高级复合 UI 应用程序尤其有用,因为细化 API 网关的概念类似于 UI 组合服务。

我们深入探讨上一部分中 基于微服务创建复合 UI 的更多详细信息。

作为关键要点,对于许多中型和大型应用程序,使用自定义生成的 API 网关产品通常是一个很好的方法,但不是单一整体式聚合器或唯一的中央自定义 API 网关,除非 API 网关允许多个独立配置区域为多个开发团队创建自治微服务。

用于通过 API 网关重新路由的示例微服务/容器

例如,eShopOnContainers 大约有六种内部微服务类型,这些微服务类型必须通过 API 网关发布,如下图所示。

服务文件夹及其子文件夹的屏幕截图。

图 6-29. Visual Studio 中 eShopOnContainers 解决方案中的微服务文件夹

关于身份服务,在设计中,它被排除在 API 网关路由之外,因为它是系统中唯一的横切关注点,不过,使用 Ocelot 也可以将其包含在重新路由列表的一部分。

所有这些服务当前都作为 ASP.NET 核心 Web API 服务实现,正如可以从代码中判断的那样。 让我们专注于其中一个微服务,例如目录微服务代码。

解决方案资源管理器的屏幕截图,其中显示了 Catalog.API 项目内容。

图 6-30. 示例 Web API 微服务 (目录微服务)

可以看到,目录微服务是典型的 ASP.NET 核心 Web API 项目,其中包含多个控制器和方法,如以下代码所示。

[HttpGet]
[Route("items/{id:int}")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
[ProducesResponseType(typeof(CatalogItem),(int)HttpStatusCode.OK)]
public async Task<IActionResult> GetItemById(int id)
{
    if (id <= 0)
    {
        return BadRequest();
    }
    var item = await _catalogContext.CatalogItems.
                                          SingleOrDefaultAsync(ci => ci.Id == id);
    //…

    if (item != null)
    {
        return Ok(item);
    }
    return NotFound();
}

HTTP 请求最终将会执行这类 C# 代码,以访问微服务数据库以及进行任何其他必要的操作。

对于微服务 URL,当容器部署在您的本地开发电脑(即本地 Docker 主机)时,每个微服务的容器始终在其 dockerfile 中指定一个内部端口(通常为端口 80),在下述的 dockerfile 中可以看到:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80

代码中显示的端口 80 在 Docker 主机内部,因此客户端应用无法访问它。

客户端应用只能在部署 docker-compose时访问已发布的外部端口(如果有)。

部署到生产环境时,不应发布这些外部端口。 出于此具体原因,为何要使用 API 网关,以避免客户端应用和微服务之间的直接通信。

但是,在开发时,需要直接访问微服务/容器,并通过 Swagger 运行它。 这就是为什么在 eShopOnContainers 中,即使 API 网关或客户端应用不使用外部端口,也仍会指定外部端口。

目录微服务的docker-compose.override.yml文件示例如下:

catalog-api:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - ASPNETCORE_URLS=http://0.0.0.0:80
    - ConnectionString=YOUR_VALUE
    - ... Other Environment Variables
  ports:
    - "5101:80"   # Important: In a production environment you should remove the external port (5101) kept here for microservice debugging purposes.
                  # The API Gateway redirects and access through the internal port (80).

可以在docker-compose.override.yml配置目录容器的内部端口为端口 80,但外部访问端口为 5101。 但是,当使用 API 网关时,应用程序不应使用此端口,只能调试、运行和测试目录微服务。

通常,不会使用 docker-compose 部署到生产环境中,因为微服务的正确生产部署环境是 Kubernetes 或 Service Fabric 等业务流程协调程序。 部署到这些环境时,将使用不同的配置文件,其中不会直接发布微服务的任何外部端口,但始终使用 API 网关中的反向代理。

在本地 Docker 主机中运行目录微服务。 从 Visual Studio 中运行完整的 eShopOnContainers 解决方案(这种解决方案会运行 docker-compose 文件中的所有服务),或者在 CMD 或 PowerShell 中使用以下 docker-compose 命令启动 Catalog 微服务,前提是你的位置在放置 docker-compose.ymldocker-compose.override.yml 的文件夹中。

docker-compose run --service-ports catalog-api

此命令仅运行 catalog-api 服务容器以及docker-compose.yml中指定的依赖项。 在这种情况下,SQL Server 容器和 RabbitMQ 容器。

然后,可以直接访问目录微服务,并通过 Swagger UI 直接通过该“外部”端口访问其方法,在本例 http://host.docker.internal:5101/swagger中:

显示 Catalog.API REST API 的 Swagger UI 的屏幕截图。

图 6-31. 使用 Swagger UI 测试目录微服务

此时,可以在 Visual Studio 中的 C# 代码中设置断点,使用 Swagger UI 中公开的方法测试微服务,最后使用 docker-compose down 命令清理所有内容。

但是,在这种情况下,通过外部端口 5101 与微服务的直接访问通信正是要在应用程序中避免的。 通过设置 API 网关的附加间接级别(在本例中为 Ocelot),可以避免这种情况。 这样,客户端应用就不会直接访问微服务。

使用 Ocelot 实现 API 网关

Ocelot 基本上是一组中间件,可以按特定顺序应用。

Ocelot 旨在专门与 ASP.NET Core 一起使用。 包的最新版本是面向 .NET 6 的 18.0,因此不适合 .NET Framework 应用程序。

在 Visual Studio 中使用 Ocelot 的 NuGet 包在 ASP.NET Core 项目中安装 Ocelot 及其依赖项。

Install-Package Ocelot

在 eShopOnContainers 中,其 API 网关实现是一个简单的 ASP.NET 核心 WebHost 项目,Ocelot 的中间件处理所有 API 网关功能,如下图所示:

解决方案资源管理器的屏幕截图,其中显示了 Ocelot API 网关项目。

图 6-32. eShopOnContainers 中的 OcelotApiGw 基础项目

此 ASP.NET Core WebHost 项目是使用两个简单的文件生成的: Program.csStartup.cs

Program.cs只需创建和配置典型的 ASP.NET Core BuildWebHost。

namespace OcelotApiGw
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args)
        {
            var builder = WebHost.CreateDefaultBuilder(args);

            builder.ConfigureServices(s => s.AddSingleton(builder))
                    .ConfigureAppConfiguration(
                          ic => ic.AddJsonFile(Path.Combine("configuration",
                                                            "configuration.json")))
                    .UseStartup<Startup>();
            var host = builder.Build();
            return host;
        }
    }
}

Ocelot 的关键点是你必须通过 configuration.json 方法向构建器提供 AddJsonFile() 文件。 在那里,您可以指定所有 API 网关的重路由,这意味着指定具有特定端口的外部端点以及使用不同端口的相关内部端点。

{
    "ReRoutes": [],
    "GlobalConfiguration": {}
}

配置分为两个部分。 包含多个 ReRoutes 以及一个 GlobalConfiguration 的数组。 ReRoutes 是指示 Ocelot 如何处理上游请求的对象。 全局配置允许覆盖 ReRoute 特定设置。 如果不想管理大量特定于 ReRoute 的设置,则非常有用。

下面是 eShopOnContainers 中某个 API 网关 的 ReRoute 配置文件 的简化示例。

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "catalog-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/c/{everything}",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ]
    },
    {
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "basket-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/b/{everything}",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "IdentityApiKey",
        "AllowedScopes": []
      }
    }

  ],
    "GlobalConfiguration": {
      "RequestIdKey": "OcRequestId",
      "AdministrationPath": "/administration"
    }
  }

Ocelot API 网关的主要功能是获取传入的 HTTP 请求并将其转发到下游服务,当前作为另一个 HTTP 请求。 Ocelot 将一个请求的路由描述为 ReRoute。

例如,让我们专注于上述 configuration.json 中的一个 ReRoutes,即 Basket 微服务的配置。

{
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "basket-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/b/{everything}",
      "UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "IdentityApiKey",
        "AllowedScopes": []
      }
}

DownstreamPathTemplate、Scheme 和 DownstreamHostAndPorts 组成将此请求转发到的内部微服务 URL。

端口是服务使用的内部端口。 使用容器时,需在其 dockerfile 中明确指定端口。

Host的服务名称取决于您所使用的服务名称解析。 使用 docker-compose 时,服务名称由 Docker 主机提供,后者使用 docker-compose 文件中提供的服务名称。 如果使用 Kubernetes 或 Service Fabric 等业务流程协调程序,则应通过每个业务流程协调程序提供的 DNS 或名称解析来解析该名称。

DownstreamHostAndPorts 是一个数组,其中包含要转发请求的任何下游服务的主机和端口。 通常,此配置将只包含一个条目,但有时你可能想要对下游服务的请求进行负载均衡,而 Ocelot 允许你添加多个条目,然后选择负载均衡器。 但是,如果使用 Azure 和任何业务流程协调程序,最好是使用云和业务流程协调程序基础结构进行负载均衡。

Ocelot 使用 UpstreamPathTemplate URL 来确定对于客户端给定请求应该使用哪个 DownstreamPathTemplate。 最后,使用 UpstreamHttpMethod 允许 Ocelot 将发往同一 URL 的不同请求(GET、POST、PUT)区分开来。

此时,可以使用一个或多个 合并 configuration.json 文件 创建单个 Ocelot API 网关(ASP.NET Core WebHost),也可以将 配置存储在 Consul KV 存储中

但正如体系结构和设计部分中介绍的那样,如果真的想要拥有自治微服务,最好将单个单一 API 网关拆分为多个 API 网关和/或 BFF(前端后端)。 为此,让我们了解如何使用 Docker 容器实现该方法。

使用单个 Docker 容器映像运行多个不同的 API 网关/BFF 容器类型

在 eShopOnContainers 中,我们将单个 Docker 容器映像与 Ocelot API 网关配合使用,但在运行时,我们通过提供不同的 configuration.json 文件为每种 API-Gateway/BFF 创建不同的服务/容器,从而使用 docker 卷访问每个服务的不同电脑文件夹。

用于所有 API 网关的单一 Ocelot 网关 Docker 映像的示意图。

图 6-33. 跨多个 API 网关类型重用单个 Ocelot Docker 映像

在 eShopOnContainers 中,“通用 Ocelot API 网关 Docker 镜像”通过名为“OcelotApiGw”的项目以及在docker-compose.yml文件中指定的映像名称“eshop/ocelotapigw”被创建。 然后,部署到 Docker 时,将从同一 Docker 映像创建四个 API-Gateway 容器,如以下docker-compose.yml文件中的提取所示。

  mobileshoppingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

  mobilemarketingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

  webshoppingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

  webmarketingapigw:
    image: eshop/ocelotapigw:${TAG:-latest}
    build:
      context: .
      dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile

此外,如以下docker-compose.override.yml文件中所示,这些 API 网关容器之间的唯一区别是 Ocelot 配置文件,该文件对于每个服务容器都是不同的,并且它是在运行时通过 Docker 卷指定的。

mobileshoppingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5200:80"
  volumes:
    - ./src/ApiGateways/Mobile.Bff.Shopping/apigw:/app/configuration

mobilemarketingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5201:80"
  volumes:
    - ./src/ApiGateways/Mobile.Bff.Marketing/apigw:/app/configuration

webshoppingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5202:80"
  volumes:
    - ./src/ApiGateways/Web.Bff.Shopping/apigw:/app/configuration

webmarketingapigw:
  environment:
    - ASPNETCORE_ENVIRONMENT=Development
    - IdentityUrl=http://identity-api
  ports:
    - "5203:80"
  volumes:
    - ./src/ApiGateways/Web.Bff.Marketing/apigw:/app/configuration

由于上述代码,如下面的 Visual Studio 资源管理器所示,定义每个特定业务/BFF API 网关所需的唯一文件只是一个 configuration.json 文件,因为四个 API 网关基于相同的 Docker 映像。

显示包含 configuration.json 文件的所有 API 网关的屏幕截图。

图 6-34. 使用 Ocelot 定义每个 API 网关/BFF 所需的唯一文件是配置文件

通过将 API 网关拆分为多个 API 网关,专注于不同微服务子集的不同开发团队可以使用独立的 Ocelot 配置文件来管理自己的 API 网关。 此外,还可以重复使用相同的 Ocelot Docker 映像。

现在,如果您使用 API 网关运行 eShopOnContainers(在打开 eShopOnContainers-ServicesAndWebApps.sln 解决方案时,VS 默认情况下包含这些网关),或者通过运行“docker-compose up”,将会执行以下示例路由。

例如,访问 Webshoppingapigw API 网关提供的上游 URL http://host.docker.internal:5202/api/v1/c/catalog/items/2/ 时,可以从 Docker 主机中的内部下游 URL http://catalog-api/api/v1/2 获取相同的结果,如以下浏览器所示。

浏览器中显示出通过 API 网关的响应的屏幕截图。

图 6-35. 通过 API 网关提供的 URL 访问微服务

由于测试或调试的需要,如果您想直接访问Docker目录容器(仅限于开发环境),而不经过API网关,因为“catalog-api”是Docker主机内部的DNS解析(docker-compose服务名称负责的服务发现),直接访问容器的唯一方法是通过在docker-compose.override.yml中发布的外部端口。这种方法仅适用于开发测试,例如在以下浏览器中使用http://host.docker.internal:5101/api/v1/Catalog/items/1

显示对 Catalog.api 的直接响应的浏览器屏幕截图。

图 6-36. 直接访问微服务以进行测试

但是,应用程序被配置为通过 API 网关访问所有微服务,而不是通过端口快捷方式。

eShopOnContainers 中的网关聚合模式

如前所述,实现请求聚合的灵活方法是按代码使用自定义服务。 在 eShopOnContainers 中实现聚合的选定方法是为每个聚合器使用显式 ASP.NET 核心 Web API 服务。

根据这种方法,在考虑前面所示简化的全局体系结构关系图中未显示的聚合器服务时,API 网关组合关系图实际上会更加扩展。

在下图中,还可以查看聚合器服务如何使用其相关的 API 网关。

显示聚合器服务的 eShopOnContainers 体系结构示意图。

图 6-37. 具有聚合器服务的 eShopOnContainers 体系结构

在下图中的“购物”业务区域进一步放大,可以看到,在 API 网关中使用聚合器服务时,客户端应用与微服务之间的聊天会减少。

显示 eShopOnContainers 体系结构放大的示意图。

图 6-38. 聚焦聚合器服务的愿景

可以注意到,当图示显示来自 API 网关的可能请求时,复杂性就会显现。 另一方面,使用聚合器模式时,可以看到蓝色箭头如何从客户端应用的角度简化通信。 此模式不仅有助于减少通信中的聊天性和延迟,还显著改善了远程应用(移动和 SPA 应用)的用户体验。

对于“营销”业务区域和微服务,这是一个简单的用例,因此无需使用聚合器,但也可以根据需要使用。

Ocelot API 网关中的身份验证和授权

在 Ocelot API 网关中,您可以将身份验证服务,比如使用 IdentityServer 提供身份验证令牌的 ASP.NET Core Web API 服务,设置在 API 网关的外部或内部。

由于 eShopOnContainers 使用具有基于 BFF 和业务区域的边界的多个 API 网关,因此标识/身份验证服务被排除在 API 网关之外,如下图所示,用黄色突出显示。

显示 API 网关下的身份微服务图示。

图 6-39. eShopOnContainers 中标识服务的位置

但是,Ocelot 还支持将 Identity/Auth 微服务置于 API 网关内,如其他示意图所示。

Ocelot API 网关中的身份验证图示。

图 6-40. Ocelot 中的身份验证

如上图所示,当标识微服务位于 API 网关 (AG): 1) AG 从标识微服务请求身份验证令牌时,2) 标识微服务使用身份验证令牌将令牌返回到 AG,3-4) AG 请求从微服务使用身份验证令牌。 由于 eShopOnContainers 应用程序已将 API 网关拆分为多个 BFF(前端后端)和业务领域 API 网关,因此另一种选择是创建额外的 API 网关,以便进行交叉关注。 在更复杂的以微服务为基础的体系结构中,这种选择在应对多个交叉关注点微服务的情况下是公平的。 由于 eShopOnContainers 中只有一个横切关注点,所以为了简单起见,决定仅在 API 网关之外处理安全相关服务。

在任何情况下,如果应用在 API 网关级别受到保护,则尝试使用任何受保护的微服务时,首先会访问 Ocelot API 网关的身份验证模块。 这会将 HTTP 请求重定向到访问标识或身份验证微服务以获取访问令牌,以便可以使用access_token访问受保护的服务。

通过身份验证来保证任何服务在 API 网关级别上的安全性的方法是在 configuration.json的相关设置项中配置 AuthenticationProviderKey。

    {
      "DownstreamPathTemplate": "/api/{version}/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "basket-api",
          "Port": 80
        }
      ],
      "UpstreamPathTemplate": "/api/{version}/b/{everything}",
      "UpstreamHttpMethod": [],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "IdentityApiKey",
        "AllowedScopes": []
      }
    }

Ocelot 运行时,它将查看 ReRoutes AuthenticationOptions.AuthenticationProviderKey,并检查是否存在使用给定密钥注册的身份验证提供程序。 如果没有,则 Ocelot 将不会启动。 如果有,则 ReRoute 将在执行时使用该提供程序。

由于 Ocelot WebHost 已配置 authenticationProviderKey = "IdentityApiKey",因此该服务在收到没有身份验证令牌的请求时,需要进行身份验证。

namespace OcelotApiGw
{
    public class Startup
    {
        private readonly IConfiguration _cfg;

        public Startup(IConfiguration configuration) => _cfg = configuration;

        public void ConfigureServices(IServiceCollection services)
        {
            var identityUrl = _cfg.GetValue<string>("IdentityUrl");
            var authenticationProviderKey = "IdentityApiKey";
                         //…
            services.AddAuthentication()
                .AddJwtBearer(authenticationProviderKey, x =>
                {
                    x.Authority = identityUrl;
                    x.RequireHttpsMetadata = false;
                    x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                    {
                        ValidAudiences = new[] { "orders", "basket", "locations", "marketing", "mobileshoppingagg", "webshoppingagg" }
                    };
                });
            //...
        }
    }
}

然后,您需要通过在要访问的任何资源(例如微服务)上使用 [Authorize] 属性来设置授权,例如以下购物篮微服务控制器。

namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
{
    [Route("api/v1/[controller]")]
    [Authorize]
    public class BasketController : Controller
    {
      //...
    }
}

ValidAudiences(例如“basket”)与 Startup 类的 ConfigureServices()中的每个微服务 AddJwtBearer() 中定义的受众相关,如下面的代码。

// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

var identityUrl = Configuration.GetValue<string>("IdentityUrl");

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(options =>
{
    options.Authority = identityUrl;
    options.RequireHttpsMetadata = false;
    options.Audience = "basket";
});

如果尝试访问任何受保护的微服务,例如基于 API 网关 http://host.docker.internal:5202/api/v1/b/basket/1的 ReRoute URL 的购物篮微服务,则除非提供有效的令牌,否则会收到 401 未授权。 另一方面,如果对 ReRoute URL 进行身份验证,Ocelot 将调用与之关联的下游方案(内部微服务 URL)。

Ocelot 的 ReRoutes 层的授权。 Ocelot 支持在身份验证之后评估的基于声明的授权机制。 通过将以下行添加到 ReRoute 配置,在路由级别设置授权。

"RouteClaimsRequirement": {
    "UserType": "employee"
}

在该示例中,当调用授权中间件时,Ocelot 会发现用户是否在令牌中具有声明类型“UserType”,以及该声明的值是否为“employee”。 如果条件不满足,则用户将不会被授权,并且响应将是 403 禁止访问。

使用 Kubernetes Ingress 以及 Ocelot API 网关

使用 Kubernetes 时(例如在 Azure Kubernetes 服务群集中),通常基于 Nginx 通过 Kubernetes 入口层 来统一所有的 HTTP 请求。

在 Kubernetes 中,如果不使用任何入口方式,那么服务和 Pod 的 IP 只能由集群网络路由。

但是,如果使用入口方法,则 Internet 与服务(包括 API 网关)之间存在中间层,充当反向代理。

作为定义,入口是允许入站连接访问群集服务的规则集合。 入口配置为提供外部可访问的 URL、流量负载均衡、SSL 结束等服务。 用户通过将入口资源 POS 到 API 服务器来请求入口。

在 eShopOnContainers 中,在本地开发和仅使用开发计算机作为 Docker 主机时,不会使用任何入口,而只使用多个 API 网关。

但是,当针对基于 Kubernetes 的“生产”环境时,eShopOnContainers 将使用 API 网关前面的入口。 这样,客户端仍调用相同的基 URL,但请求将路由到多个 API 网关或 BFF。

API 网关是用于展示服务的前端接口或外壳,不涉及通常在其职责范围之外的 Web 应用程序。 此外,API 网关可能会隐藏某些内部微服务。

但是,入口只是重定向 HTTP 请求,但不尝试隐藏任何微服务或 Web 应用。

在 Web 应用程序前面在 Kubernetes 中拥有入口 Nginx 层以及多个 Ocelot API 网关/BFF 是理想的体系结构,如下图所示。

显示入口层如何集成到 AKS 环境中的关系图。

图 6-41. 部署到 Kubernetes 时,eShopOnContainers 中的入口层

Kubernetes 入口充当为了应用程序而处理所有流量的反向代理,包括不在 Api 网关范围内的 Web 应用程序。 将 eShopOnContainers 部署到 Kubernetes 时,它仅通过 入口暴露几个服务或终结点,基本上是 URL 上的以下后缀列表:

  • / 用于客户端 SPA Web 应用程序
  • /webmvc 用于客户端 MVC Web 应用程序的
  • /webstatus 用于显示状态/健康检查的客户端网页应用
  • /webshoppingapigw 用于 Web BFF 和购物业务流程
  • /webmarketingapigw 用于 Web BFF 和营销业务流程
  • /mobileshoppingapigw 适用于移动 BFF 和购物业务流程
  • /mobilemarketingapigw 适用于移动 BFF 和营销业务流程

部署到 Kubernetes 时,每个 Ocelot API 网关都对运行 API 网关的每个 Pod 使用不同的“configuration.json”文件。 这些“configuration.json”文件是通过装载(最初使用 deploy.ps1 脚本)基于名为“ocelot”的 Kubernetes 配置映射 创建的卷提供的。 每个容器在其名为 /app/configuration 的容器文件夹中加载其相关配置文件。

在 eShopOnContainers 的源代码文件中,可以在文件夹中找到 k8s/ocelot/ 原始的“configuration.json”文件。 每个 BFF/APIGateway 都有一个文件。

Ocelot API 网关中的其他交叉功能

使用 Ocelot API 网关时,还需要研究和使用其他重要功能,详细信息请参见以下链接。