如何使用 OpenAPI 为 AWS API 网关配置 CORS?

How to configure CORS for AWS API Gateway using OpenAPI?

我有一个 api 的 OpenAPI 规范,我正在通过 CDK 进行部署。规格如下:

openapi: 3.0.1
info:
  title: My API
  description: My REST API with CORS enabled
  version: 0.1.0

x-amazon-apigateway-cors:
  allowOrigins:
    - "*"
  allowCredentials: true
  exposeHeaders:
    - "x-apigateway-header"
    - "x-amz-date"
    - "content-type"
  maxAge: 3600
  allowMethods:
    - "*"
  allowHeaders":
    - "x-apigateway-header"
    - "x-amz-date"
    - "content-type"
    - "Authorization"

components:
  securitySchemes:
    lambda:
      type: "apiKey"
      name: "Authorization"
      in: "header"
      x-amazon-apigateway-authtype: "custom"
      x-amazon-apigateway-authorizer:
        authorizerUri: "{{my-lambda-authorizer}}"
        authorizerResultTtlInSeconds: 300
        type: "token" 

paths:
  /user/{id}:
    get:
      summary: Get info of specified user.
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
      security:
        - lambda: []
      x-amazon-apigateway-integration:
        uri: "{{my-lambda}}"
        passthroughBehavior: "when_no_match"
        httpMethod: "POST"
        type: "aws_proxy"

当我尝试通过 fetch() 访问它时,出现错误 Failed to load resource: Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin

fetch('https://api.example.com/user/1')
        .then(response => response.json())
        .then((user: User) => {
            // do something
        })
        .catch((err) => {
            console.log("Error: " + err);
        });

API 可在 api.example.com 访问,我是 运行 在本地使用 Gatsby 的网站 localhost:8000

当我将 x-amazon-apigateway-cors 放在规范的根目录时,AWS docs 似乎表明 CORS 已启用,但当我尝试时,CORS 似乎未启用(或工作)访问 API。如何为我的 API 启用 CORS 而无需在控制台中进行配置?

Amazon API 网关提供两种类型的 APIs:REST APIs 和 HTTP APIs。 REST APIs 是 Amazon API Gateway 最初引入的那种 APIs,而 HTTP APIs got announced at the end of 2019.

根据您的 OpenAPI 规范的描述,我假设您正在尝试部署 REST API。然而,x-amazon-apigateway-cors OpenAPI 扩展仅适用于 HTTP APIs。

您现在有两个选择:要么切换到使用 HTTP API,要么手动配置 CORS。只要您不需要 features only supported by REST APIs,我建议您改用 HTTP API,因为这是 API Amazon API 网关提供的更现代的类型。

如果您想使用 REST API,启用 CORS 需要更多的手动配置。 AWS 在 Enabling CORS for a REST API resource 中对此进行了记录。本质上,您必须确保您的集成 return 具有正确的 CORS headers。对于可能如下所示的 NodeJS AWS Lambda 函数:

exports.handler = async (event) => {
    const response = {
        statusCode: 200,
        headers: {
            "Access-Control-Allow-Headers" : "Content-Type",
            "Access-Control-Allow-Origin": "https://www.example.com",
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET"
        },
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

对于 CORS pre-flight 请求,您还必须确保 OPTIONS-requests 也 return 正确 headers。实现这一点的最简单方法是将 OPTIONS-request 的 mock-integration 添加到您的 OpenAPI 规范中。看起来像:

options:
  responses:
    '200':
      description: Default response
      headers:
        Access-Control-Allow-Headers:
          schema:
            type: string
        Access-Control-Allow-Methods:
          schema:
            type: string
        Access-Control-Allow-Origin:
          schema:
            type: string
  x-amazon-apigateway-integration:
    type: mock
    requestTemplates:
      application/json: |
        {"statusCode" : 200}
    responses:
      default:
        statusCode: 200
        responseParameters:
          method.response.header.Access-Control-Allow-Headers: "'*'"
          method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST'"
          method.response.header.Access-Control-Allow-Origin: "'https://example.com/'"

另请注意 modern browsers don't support localhost as origin for CORS,因此您可能也需要解决这个问题。