OpenAPI 扩展程序

Cloud Endpoints 接受一组特定于 Google 的 OpenAPI 规范扩展程序,用于配置 Extensible Service Proxy (ESP)Extensible Service Proxy V2 (ESPv2)Service Control 的行为。本页介绍了特定于 Google 的 OpenAPI 规范扩展程序。

尽管下面提供的示例采用了 YAML 格式,但也支持 JSON。

命名惯例

Google OpenAPI 扩展程序的名称以 x-google- 前缀开头。

x-google-allow

x-google-allow: [configured | all]

此扩展程序用于 OpenAPI 规范的顶层,可用来指示应允许哪些网址路径通过 ESP。

可能的值有 configuredall

默认值为 configured,这表示只有您在 OpenAPI 规范中列出的 API 方法可通过 ESP 提供。

如果使用 all,则未配置的调用(无论是否使用 API 密钥或用户身份验证)会通过 ESP 传递给您的 API。

ESP 以区分大小写的方式处理对 API 的调用。 例如,ESP 将 /widgets/Widgets 视为不同的 API 方法。

使用 all 时,您需要特别注意以下两个方面:

  • 任何 API 密钥或身份验证规则。
  • 服务中的后端路径路由。

我们建议您将 API 配置为使用区分大小写的路径路由,这是一种最佳做法。在使用区分大小写的路由后,如果网址中请求的方法与 OpenAPI 规范中列出的 API 方法名称不匹配,您的 API 会返回 HTTP 状态代码 404。请注意,Node.js Express 等 Web 应用框架有一项设置用于启用或停用区分大小写的路由。默认行为取决于您所使用的框架。建议您查看框架中的设置,以确保启用区分大小写的路由。这一建议符合 OpenAPI 规范 2.0 版,该规范指出:“规范中的所有字段名称都区分大小写”。

示例

假设:

  • x-google-allow 设置为 all
  • OpenAPI 规范中列有 API 方法 widgets,而非 Widgets
  • 您已将 OpenAPI 规范配置为需要 API 密钥。

由于 OpenAPI 规范中列有 widgets,因此 ESP 会阻止以下请求,因为该请求不具有 API 密钥:

https://my-project-id.appspot.com/widgets

由于 OpenAPI 规范中未列出 Widgets,因此 ESP 会将以下请求传递给您的服务,而无需 API 密钥:

https://my-project-id.appspot.com/Widgets/

如果 API 使用区分大小写的路由(并且您未将对“Widgets”的调用路由到任何代码),则 API 后端将返回 404。不过,如果您使用不区分大小写的路由,则 API 后端会将此调用路由到“widgets”。

不同的语言和框架采用不同的方法来控制大小写区分和路由。如需了解详情,请参阅框架的相关文档。

x-google-backend

x-google-backend 扩展程序指定如何将请求路由到本地后端或远程后端。可以在 OpenAPI 规范的顶层和/或操作级别指定扩展程序。

默认情况下,ESP 配置为将所有流量代理至单个本地后端。本地后端地址由 --backend 标志(默认为 http://127.0.0.1:8081)指定。您可以使用 x-google-backend 扩展程序替换此默认行为,并指定可接收请求的一个或多个本地后端或远程后端。

x-google-backend 扩展程序还可以为本地后端和远程后端配置其他设置,例如身份验证和超时。所有这些配置都可以按照每项操作的方式应用。

x-google-backend 扩展程序包含以下字段:

address

address: URL

可选。目标后端的网址。 该网址所用架构必须是 httphttps

路由到远程后端(无服务器)时,应设置此网址,其架构部分应为 https

如果操作使用 x-google-backend,但未指定 address,则 ESPv2 会将请求路由到 --backend 标志指定的本地后端。

jwt_audience | disable_auth

这两项属性只能设置其中一项。

如果操作使用 x-google-backend,但未指定 jwt_audiencedisable_auth,则 ESPv2 将自动默认使用 jwt_audience 以匹配 address。如果未设置 address,则 ESPv2 会自动将 disable_auth 设置为 true

jwt_audience

jwt_audience: string

可选。ESPv2 获取实例 ID 令牌时指定的 JWT 目标对象,然后供发出目标后端请求时使用。

配置无服务器端点时,远程后端应配置为仅允许来自 ESPv2 的流量。在代理请求时,ESPv2 会将实例 ID 令牌附加到 Authorization 标头。实例 ID 令牌代表用于部署 ESPv2 的运行时服务账号。随后,远程后端可以根据附加的令牌来验证请求是否来自 ESPv2。

例如,部署在 Cloud Run 上的远程后端可以使用 IAM 执行以下操作:

  1. 通过撤消特殊 allUsers 主账号中的 roles/run.invoker 来限制未经身份验证的调用。
  2. 通过向 ESPv2 运行时服务账号授予 roles/run.invoker 角色,仅允许 ESPv2 调用后端。

默认情况下,ESPv2 会创建一个实例 ID 令牌,其 JWT 目标对象与 address 字段匹配。仅当目标后端使用基于 JWT 的身份验证且预期目标与 address 字段中指定的值不同时,才需要手动指定 jwt_audience。对于部署在 App Engine 上或使用 IAP 的远程后端,您必须替换 JWT 目标对象。App Engine 和 IAP 使用其 OAuth 客户端 ID 作为预期目标对象。

启用此功能后,ESPv2 将更改请求中的标头。如果请求已设置 Authorization 标头,则 ESPv2 将执行以下操作:

  1. 将原始值复制到新标头 X-Forwarded-Authorization
  2. Authorization 标头替换为实例 ID 令牌。

因此,如果 API 客户端设置了 Authorization 标头,则在 ESPv2 后运行的后端应使用 X-Forwarded-Authorization 标头来检索整个 JWT。后端必须验证此标头中的 JWT,因为如果未配置身份验证方法,ESPv2 将不会执行验证。

disable_auth

disable_auth: bool

可选。此属性确定 ESPv2 是否应禁止获取实例 ID 令牌并阻止将其附加到请求中。

在配置目标后端时,如果满足以下任一条件,您可能不想使用 IAP 或 IAM 对来自 ESPv2 的请求进行身份验证:

  1. 后端应允许未经身份验证的调用。
  2. 后端需要 API 客户端的原始 Authorization 标头,并且不能使用 X-Forwarded-Authorization(如 jwt_audience 部分所述)。

在这种情况下,请将此字段设为 true

path_translation

path_translation: [ APPEND_PATH_TO_ADDRESS | CONSTANT_ADDRESS ]

可选。设置在将请求发送到目标后端时,ESPv2 使用的路径转换策略。

如需详细了解路径转换,请参阅了解路径转换部分。

x-google-backend 用于 OpenAPI 规范的顶层时,path_translation 默认为 APPEND_PATH_TO_ADDRESS;而当 x-google-backend 用于 OpenAPI 规范的操作级层时,path_translation 默认为 CONSTANT_ADDRESS。如果无 address 字段,则 path_translation 将处于未指定状态,并且不会发生相应的操作。

deadline

deadline: double

可选。等待请求完整响应的秒数。 超过配置的截止期限的响应会超时。 默认截止期限为 15.0 秒。

系统不接受非正值。在这些情况下,ESPv2 会自动使用默认值。

无法停用截止期限,但可以将其设置为较高的数字,例如 3600.0 表示一小时。

protocol

protocol: [ http/1.1 | h2 ]

可选。用于向后端发送请求的协议。 支持的值为 http/1.1h2

HTTP 和 HTTPS 后端的默认值为 http/1.1

对于支持 HTTP/2 的安全 HTTP 后端 (https://),请将此字段设置为 h2 以提升性能。 对于 Google Cloud 无服务器后端,这是推荐的选项。

在 ESP 中启用后端支持

ESPv2 将会自动检测何时配置了 x-google-backend

ESP 需要手动更改配置才能启用此功能。要在 ESP 中启用 x-google-backend 支持,请在运行 ESP 容器时提供 --enable_backend_routing 参数。 (对于您无法控制 ESP 容器选项的运行时,系统已为您提供此选项。)下面的示例展示了如何在将 ESP 容器部署到 GKE 时启用 x-google-backend 支持(此示例基于“GKE 上的 Endpoints”教程示例构建):

- name: esp
  image: gcr.io/endpoints-release/endpoints-runtime:1
  args: [
    "--http_port", "8081",
    "--service", "SERVICE_NAME",
    "--rollout_strategy", "managed",
    "--enable_backend_routing"
  ]

了解路径转换

当 ESP 处理请求时,它将采用原始请求路径并对其进行转换,然后才会向目标后端发出请求。至于这种转换具体是如何发生的,则取决于您所使用的路径转换策略。有两种路径转换策略:

  • APPEND_PATH_TO_ADDRESS:将原始请求路径附加到 x-google-backend 扩展程序的 address 网址,借此计算目标后端请求路径。
  • CONSTANT_ADDRESS:目标请求路径是常量,由 x-google-backend 扩展程序的 address 网址定义。如果相应的 OpenAPI 路径包含参数,则参数名称及其值会变为查询参数。

示例:

  • APPEND_PATH_TO_ADDRESS
    • address: https://my-project-id.appspot.com/BASE_PATH
    • 带 OpenAPI 路径参数
      • OpenAPI 路径:/hello/{name}
      • 请求路径:/hello/world
      • 目标请求网址:https://my-project-id.appspot.com/BASE_PATH/hello/world
    • 不带 OpenAPI 路径参数
      • OpenAPI 路径:/hello
      • 请求路径:/hello
      • 目标请求网址:https://my-project-id.appspot.com/BASE_PATH/hello
  • CONSTANT_ADDRESS
    • addresshttps://us-central1-my-project-id.cloudfunctions.net/helloGET
    • 带 OpenAPI 路径参数
      • OpenAPI 路径:/hello/{name}
      • 请求路径:/hello/world
      • 目标请求网址:https://us-central1-my-project-id.cloudfunctions.net/helloGET?name=world
    • 不带 OpenAPI 路径参数
      • OpenAPI 路径:/hello
      • 请求路径:/hello
      • 目标请求网址:https://us-central1-my-project-id.cloudfunctions.net/helloGET

x-google-endpoints

本部分介绍 x-google-endpoints 扩展程序的用法。

cloud.goog 网域上配置 DNS

如果您已将应用部署到 Compute Engine 或 Google Kubernetes Engine,则可以为 cloud.goog 网域上的 Endpoints 服务创建 DNS 条目,具体方法是向 OpenAPI 文档添加以下内容:

x-google-endpoints:
- name: "API_NAME.endpoints.PROJECT_ID.cloud.goog"
  target: "IP_ADDRESS"

在 OpenAPI 文档的顶层添加 x-google-endpoints 扩展程序(不缩进或嵌套)。您必须按以下格式配置域名:.endpoints.PROJECT_ID.cloud.goog

例如:

swagger: "2.0"
host: "my-cool-api.endpoints.my-project-id.cloud.goog"
x-google-endpoints:
- name: "my-cool-api.endpoints.my-project-id.cloud.goog"
  target: "192.0.2.1"

.cloud.goog 是由 Google 管理的网域,可供 Google Cloud 客户共用。由于 Google Cloud 项目 ID 在全局范围内不会重复,因此 .endpoints.PROJECT_ID.cloud.goog 格式的域名是您的 API 的专属域名。

为简单起见,请将 host 字段和 x-google-endpoints.name 字段配置为相同的值。部署 OpenAPI 文档时,Service Management 会创建以下内容:

  • 托管式服务(使用您在 host 字段中指定的名称)。
  • DNS A 记录(使用您在 x-google-endpoints 扩展程序中配置的名称和 IP 地址)。

对于在 App Engine 柔性环境中托管的 API,您可以使用 appspot.com 网域。如需了解详情,请参阅配置 Endpoints

配置 ESP 以允许 CORS 请求

如果从不同来源的网页应用调用您的 API,您的 API 必须支持跨源资源共享 (CORS)。如需了解如何配置 ESP 以支持 CORS,请参阅向 ESP 添加 CORS 支持

如果您需要在后端代码中实现自定义 CORS 支持,请设置 allowCors: True,这样 ESP 便可将所有 CORS 请求传递到您的后端代码:

x-google-endpoints:
- name: "API_NAME.endpoints.PROJECT_ID.cloud.goog"
  allowCors: True

请在 OpenAPI 文档的顶层(不使用缩进或嵌套结构)添加 x-google-endpoints 扩展程序,例如:

swagger: "2.0"
host: "my-cool-api.endpoints.my-project-id.cloud.goog"
x-google-endpoints:
- name: "my-cool-api.endpoints.my-project-id.cloud.goog"
  allowCors: True

x-google-issuer

x-google-issuer: URI | EMAIL_ADDRESS

此扩展程序用于 OpenAPI securityDefinitions 部分,可用来指定凭据的签发者。值可以采用主机名或电子邮件地址的形式。

x-google-jwks_uri

x-google-jwks_uri: URI

提供商公钥集的 URI,用于验证 JSON Web 令牌的签名。

ESP 支持 x-google-jwks_uri OpenAPI 扩展定义的两种非对称公钥格式:

  • JWK 集格式。 例如:
    x-google-jwks_uri: "https://YOUR_ACCOUNT_NAME.YOUR_AUTH_PROVIDER_URL/.well-known/jwks.json"
    
  • X509。例如:
    x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@system.gserviceaccount.com"
    

如果您使用的是对称密钥格式,请将 x-google-jwks_uri 设置为包含 base64url 编码密钥字符串的文件的 URI。

如果省略 x-google-jwks_uri,ESP 将遵循 OpenID Connect Discovery 协议自动发现给定 OpenID 提供商的 JWKS URI。ESP 将向 x-google-issuer/.well-known/openid-configuration 发送请求,解析 JSON 响应,以及从顶级 jwks_uri 字段读取 JWKS URI。

请注意,省略 x-google-jwks_uri 会导致较长的冷启动时间,因为 ESP 必须在启动时进行额外的远程调用。因此,如果 JWKS URI 经常发生更改,建议您仅省略此字段。 大多数认证的 OpenID 提供商(例如 Google、Auth0 和 Okta)拥有稳定的 JWKS URI。

x-google-jwt-locations

默认情况下,JWT 会在 Authorization 标头(以 "Bearer " 为前缀)、X-Goog-Iap-Jwt-Assertion 标头或 access_token 查询参数中传递。有关传递 JWT 的示例,请参阅向 Endpoints API 发出经过身份验证的调用

或者,您也可以使用 OpenAPI SecurityDefinitions 部分中的 x-google-jwt-locations 扩展程序,以提供从中提取 JWT 令牌的自定义位置。

x-google-jwt-locations 扩展程序接受 JWT 位置列表。每个 JWT 位置都包含以下字段:

元素 说明
header/query 必需。包含 JWT 的标头的名称,或包含 JWT 的查询参数的名称。
value_prefix 可选。仅适用于标头。设置 value_prefix 时,其值必须与包含 JWT 的标头值的前缀匹配。

例如:

x-google-jwt-locations:
  # Expect header "Authorization": "MyBearerToken <TOKEN>"
  - header: "Authorization"
    value_prefix: "MyBearerToken "
  # expect header "jwt-header-foo": "jwt-prefix-foo<TOKEN>"
  - header: "jwt-header-foo"
    value_prefix: "jwt-prefix-foo"
  # expect header "jwt-header-bar": "<TOKEN>"
  - header: "jwt-header-bar"
  # expect query parameter "jwt_query_bar=<TOKEN>"
  - query: "jwt_query_bar"

如果您希望仅支持部分默认 JWT 位置,请在 x-google-jwt-locations 扩展程序中明确列出这些位置。例如,要仅添加对带有 "Bearer " 前缀的 Authorization 标头的支持,请使用以下命令:

  x-google-jwt-locations:
    # Support the default header "Authorization": "Bearer <TOKEN>"
    - header: "Authorization"
      value_prefix: "Bearer "

x-google-audiences

x-google-audiences: STRING

此扩展程序用于 OpenAPI securityDefinitions 部分,可用来提供 JWT aud 字段在 JWT 身份验证期间应匹配的受众群体列表。此扩展程序接受单个字符串,其中的各个值需要用英文逗号分隔开。受众群体之间不允许有空格。如果未指定,JWT aud 字段应与 OpenAPI 文档中的 host 字段匹配,除非使用标志 --disable_jwt_audience_service_name_check。如果使用此标志且未指定 x-google-audiences,则 JWT aud 字段不会被选中。

securityDefinitions:
  google_id_token:
    type: oauth2
    authorizationUrl: ""
    flow: implicit
    x-google-issuer: "https://accounts.google.com"
    x-google-jwks_uri: "https://www.googleapis.com/oauth2/v1/certs"
    x-google-audiences: "848149964201.apps.googleusercontent.com,841077041629.apps.googleusercontent.com"

x-google-management

x-google-management 扩展程序可控制 API 管理的不同方面,并包含本部分中描述的字段。

metrics

您可以将 metrics配额x-google-quota 结合使用,以便为您的 API 配置配额。通过配额,您可以控制应用调用 API 中的方法的速率。例如:

x-google-management:
  metrics:
    - name: read-requests
      displayName: Read requests
      valueType: INT64
      metricKind: DELTA

metrics 字段包含具有以下键值对的列表:

元素 说明
name 必需。此指标的名称。通常,这是对指标进行唯一标识的请求类型(例如“read-requests”或“write-requests”)。
displayName

可选,但建议填写。此文本会显示在 Google Cloud 控制台中 Endpoints > Services 页面的配额标签页上,用于标识指标。您的 API 使用方还会在 IAM 和管理API 和服务下的配额页面上看到此文本。显示名不得超过 40 个字符。

为确保可读性,相关配额限制中的单位会自动附加到 Google Cloud 控制台中的显示名称末尾。例如,如果您为显示名称指定“Read requests”,则 Google Cloud 控制台中会显示“Read requests per minute per project”。如果未指定,则系统会在 IAM 和管理员API 和服务下的配额页面上向 API 的使用方显示“unlabeled quota”。

为了与 API 使用方在配额页面上看到的 Google 服务显示名保持一致,我们针对显示名提出以下建议:

  • 只有一个指标时,请使用“Requests”。
  • 如果您有多个指标,则每个指标都应描述请求的类型并包含“requests”一词(例如“Read requests”或“Write requests”)。
  • 当与指标关联的任何费用大于 1 时,请使用“quota units”,而不是“requests”。
valueType 必需。必须是 INT64
metricKind 必需。必须为 DELTA

quota

您可以在 quota 部分中为定义的指标指定配额限制。例如:

quota:
  limits:
    - name: read-requests-limit
      metric: read-requests
      unit: 1/min/{project}
      values:
        STANDARD: 5000

quota.limits 字段包含具有以下键值对的列表:

元素 说明
name 必需。限制的名称,该名称在服务中必须是唯一的。该名称可以包含大小写字母、数字和“-”(短划线字符),并且长度不得超过 64 个字符。
metric 必需。此限制所适用的指标的名称。此名称必须与指标名称中指定的文本匹配。如果指定的文本与指标名称不匹配,那么在您部署 OpenAPI 文档时会出现错误。
unit 必需。限制的单位。目前仅支持“1/min/{project}”,这表示系统会对每个项目强制执行该限制,并且会每分钟重置一次用量。
values 必需。指标的限制。您必须将其指定为键值对,格式如下:
STANDARD: YOUR-LIMIT-FOR-THE-METRIC
YOUR-LIMIT-FOR-THE-METRIC 替换为整数值,该整数值是针对指定单位(目前只能是每个项目每分钟)所允许的最大请求次数。例如:
values:
  STANDARD: 5000

x-google-quota

x-google-quota 扩展程序用于 OpenAPI paths 部分,可用来将 API 中的方法与指标相关联。未定义 x-google-quota 的方法没有应用配额限制。例如:

x-google-quota:
  metricCosts:
    read-requests: 1

x-google-quota 扩展程序包含以下项:

元素 说明
metricCosts 用户定义的键值对:"YOUR-METRIC-NAME": METRIC-COST
  • "YOUR-METRIC-NAME": "YOUR-METRIC-NAME" 的文本必须与定义的指标名称匹配。
  • METRIC-COST: 一个整数值,用于定义每个请求的费用。在发出请求时,关联的指标会按指定的费用递增。通过指定不同的费用,可以让各种方法按不同的速率消耗同一指标的配额。例如,如果指标的配额上限为 1000 且费用为 1,那么在超出该上限之前,调用应用每分钟可发出 1000 个请求。对于同一指标,如果费用为 2,那么在超出该上限之前,调用应用每分钟只能发出 500 个请求。

配额示例

以下示例展示了如何为读取请求和写入请求添加指标和限制。

x-google-management:
  metrics:
    # Define a metric for read requests.
    - name: "read-requests"
      displayName: "Read requests"
      valueType: INT64
      metricKind: DELTA
    # Define a metric for write requests.
    - name: "write-requests"
      displayName: "Write requests"
      valueType: INT64
      metricKind: DELTA
  quota:
    limits:
      # Rate limit for read requests.
      - name: "read-requests-limit"
        metric: "read-requests"
        unit: "1/min/{project}"
        values:
          STANDARD: 5000
      # Rate limit for write requests.
      - name: "write-request-limit"
        metric: "write-requests"
        unit: "1/min/{project}"
        values:
          STANDARD: 5000

paths:
  "/echo":
    post:
      description: "Echo back a given message."
      operationId: "echo"
      produces:
      - "application/json"
      responses:
        200:
          description: "Echo"
          schema:
            $ref: "#/definitions/echoMessage"
      parameters:
      - description: "Message to echo"
        in: body
        name: message
        required: true
        schema:
          $ref: "#/definitions/echoMessage"
      x-google-quota:
        metricCosts:
          read-requests: 1
      security:
      - api_key: []

x-google-api-name

如果您的服务仅包含一个 API,则该 API 名称与 Endpoints 服务名称相同。(Endpoints 使用您在 OpenAPI 文档的 host 字段中指定的名称作为您的服务的名称)。如果您的服务包含多个 API,那么您需要向 OpenAPI 文档添加 x-google-api-name 扩展程序,以指定 API 名称。借助 x-google-api-name 扩展程序,您可以明确地为各个 API 命名,并对每个 API 进行独立的版本控制。

例如,您可以使用下面的 OpenAPI 文档片段来配置名为 api.example.com 且包含两个 API(Producer 和 Consumer)的服务:

  • producer.yaml 中的 Producer API:

    swagger: 2.0
    host: api.example.com
    x-google-api-name: producer
    info:
      version: 1.0.3
    

  • consumer.yaml 中的 Consumer API:

    swagger: 2.0
    host: api.example.com
    x-google-api-name: consumer
    info:
      version: 1.1.0
    

您可以使用以下命令同时部署这两个 OpenAPI 文档:

gcloud endpoints services deploy producer.yaml consumer.yaml