

# API Gateway 中的 Lambda 代理集成
<a name="set-up-lambda-proxy-integrations"></a>

以下部分介绍如何使用 Lambda 代理集成。

**Topics**
+ [了解 API Gateway Lambda 代理集成](#api-gateway-create-api-as-simple-proxy)
+ [支持多值标头和查询字符串参数](#apigateway-multivalue-headers-and-parameters)
+ [用于代理集成的 Lambda 函数的输入格式](#api-gateway-simple-proxy-for-lambda-input-format)
+ [用于代理集成的 Lambda 函数的输出格式](#api-gateway-simple-proxy-for-lambda-output-format)
+ [使用 AWS CLI 为 API Gateway 设置 Lambda 代理集成](set-up-lambda-proxy-integration-using-cli.md)
+ [使用 OpenAPI 定义设置具有 Lambda 代理集成的代理资源](api-gateway-set-up-lambda-proxy-integration-on-proxy-resource.md)

## 了解 API Gateway Lambda 代理集成
<a name="api-gateway-create-api-as-simple-proxy"></a>

Amazon API Gateway Lambda 代理集成是通过设置单个 API 方法来构建 API 的简单、强大且灵活的机制。Lambda 代理集成允许客户端调用后端的单个 Lambda 函数。该函数可访问其他AWS服务的许多资源或功能，包括调用其他 Lambda 函数。

 在 Lambda 代理集成中，当客户端提交 API 请求时，API Gateway 将[事件对象](#api-gateway-simple-proxy-for-lambda-input-format)传递给集成的 Lambda 函数，但不会保留请求参数的顺序。此[请求数据](#api-gateway-simple-proxy-for-lambda-input-format)包括请求标头、查询字符串参数、URL 路径变量、负载和 API 配置数据。配置数据可以包括当前部署阶段名称、阶段变量、用户身份或授权上下文（如果有）。后端 Lambda 函数会对传入请求数据进行解析，以确定要返回的响应。要使 API Gateway 将 Lambda 输出作为 API 响应传递给客户端，Lambda 函数必须返回[此格式](#api-gateway-simple-proxy-for-lambda-output-format)的结果。

 因为 API Gateway 不过多地在 Lambda 代理集成的客户端和后端 Lambda 函数之间进行干预，所以客户端和集成的 Lambda 函数可以适应彼此的变化而不破坏 API 的现有集成设置。要实现这一点，客户端必须遵循后端 Lambda 函数所制定的应用程序协议。

 您可以为任何 API 方法设置 Lambda 代理集成。但是，当为涉及通用代理资源的 API 方法配置 Lambda 代理集成时，该集成更加有效。通用代理资源可由 `{proxy+}` 的特殊模板化路径变量和/或“捕获全部”`ANY` 方法占位符表示。客户端可以在传入请求中将输入作为请求参数或适用负载传递给后端 Lambda 函数。请求参数包括标头、URL 路径变量、查询字符串参数和适用负载。集成的 Lambda 函数会在处理请求之前验证所有输入源，如果缺少任何必需的输入，则会使用有意义的错误消息向客户端做出响应。

 在调用与通用 HTTP 方法 `ANY` 和通用资源 `{proxy+}` 集成的 API 方法时，客户端会使用特定 HTTP 方法而非 `ANY` 来提交请求。客户端还指定特定 URL 路径而非 `{proxy+}`，并包含任何所需的标头、查询字符串参数或适用负载。

 以下列表总结了使用 Lambda 代理集成的不同 API 方法的运行时行为：
+ `ANY /{proxy+}`：客户端必须选择特定 HTTP 方法，必须设置特定资源路径层次结构，并可以设置任何标头、查询字符串参数和适用负载以将数据作为输入传递给集成的 Lambda 函数。
+ `ANY /res`：客户端必须选择特定 HTTP 方法，并可以设置任何标头、查询字符串参数和适用负载以将数据作为输入传递给集成的 Lambda 函数。
+ `GET|POST|PUT|... /{proxy+}`：客户端可以设置特定资源路径层次结构、任何标头、查询字符串参数和适用负载以将数据作为输入传递给集成的 Lambda 函数。
+  `GET|POST|PUT|... /res/{path}/...`：客户端必须选择特定路径分段（针对 `{path}` 变量），并可以设置任何请求标头、查询字符串参数和适用负载以将输入数据传递给集成的 Lambda 函数。
+  `GET|POST|PUT|... /res`：客户端可以选择任何请求标头、查询字符串参数和适用负载以将输入数据传递给集成的 Lambda 函数。

 代理资源 `{proxy+}` 和自定义资源 `{custom}` 都表示为模板化路径变量。但是 `{proxy+}` 可以指路径层次结构中的任何资源，而 `{custom}` 只能指特定路径分段。例如，一个杂货店可以按品类名称、农产品类别和产品类型整理其上线产品库存。然后，该杂货店的网站可以通过自定义资源的以下模板化路径变量来表示可用产品：`/{department}/{produce-category}/{product-type}`。例如，通过 `/produce/fruit/apple` 来表示苹果，通过 `/produce/vegetables/carrot` 来表示胡萝卜。它还可以使用 `/{proxy+}` 来表示客户在其在线商店中购物时可以搜索的任何品类、任何农产品类别或任何产品类型。例如，`/{proxy+}` 可以指以下任一项：
+ `/produce`
+ `/produce/fruit`
+ `/produce/vegetables/carrot`

 要让客户搜索任何可用产品、其农产品类别和关联的商店品类，您可以公开具有只读权限的单个方法 `GET /{proxy+}`。同样地，要允许主管更新 `produce` 品类的库存，您可以设置另一个具有读/写权限的单个方法 `PUT /produce/{proxy+}`。要允许出纳员更新蔬菜的流水式总计，您可以设置一个具有读/写权限的 `POST /produce/vegetables/{proxy+}` 方法。要让商店经理对任何可用产品执行任何可能的操作，在线商店开发人员可以公开具有读/写权限的 `ANY /{proxy+}` 方法。任何情况下，在运行时，客户或员工都必须选择所选品类中给定类型的特定产品、所选品类中的特定农产品类别或特定品类。



有关设置 API Gateway 代理集成的更多信息，请参阅 [设置具有代理资源的代理集成](api-gateway-set-up-simple-proxy.md)。

 代理集成要求客户端更详细地了解后端要求。因此，要确保最佳的应用程序性能和用户体验，后端开发人员必须清楚地向客户端开发人员表明后端的要求，并在不符合要求时提供可靠的错误反馈机制。

## 支持多值标头和查询字符串参数
<a name="apigateway-multivalue-headers-and-parameters"></a>

API Gateway 支持多个具有相同名称的标头和查询字符串参数。多值标头以及单值标头和参数可以组合使用相同的请求和响应。有关更多信息，请参阅 [用于代理集成的 Lambda 函数的输入格式](#api-gateway-simple-proxy-for-lambda-input-format) 和 [用于代理集成的 Lambda 函数的输出格式](#api-gateway-simple-proxy-for-lambda-output-format)。

## 用于代理集成的 Lambda 函数的输入格式
<a name="api-gateway-simple-proxy-for-lambda-input-format"></a>

使用 Lambda 代理集成，API Gateway 可以将整个客户端请求映射到后端 Lambda 函数的输入 `event` 参数：以下示例显示了 API Gateway 发送到 Lambda 代理集成的事件的结构。

在本例中，我们假设对 API Gateway 的调用为以下内容：

```
curl 'https://a1b2c3.execute-api.us-east-1.amazonaws.com/my/path?parameter1=value1&parameter2=value1&parameter2=value2&parameter3=value1,value2' -H 'header1: value1' -H 'header2: value1' -H 'header2: value2' -H 'header3: value1,value2'
```

输出内容如下所示：

```
{
  "resource": "/my/path",
  "path": "/my/path",
  "httpMethod": "GET",
  "headers": {
      "header1": "value1",
      "header2": "value2",
      "header3": "value1,value2"
  },
  "multiValueHeaders": {
    "header1": ["value1"],
    "header2": ["value1","value2"],
    "header3": ["value1,value2"]
  },
  "queryStringParameters": {
      "parameter1": "value1",
      "parameter2": "value2",
      "parameter3": "value1,value2"
  },
  "multiValueQueryStringParameters": {
    "parameter1": ["value1"],
    "parameter2": ["value1","value2"],
    "parameter3": ["value1,value2"]
  },
  "requestContext": {
    "accountId": "123456789012",
    "apiId": "id",
    "authorizer": {
      "claims": null,
      "scopes": null
    },
    "domainName": "id.execute-api.us-east-1.amazonaws.com",
    "domainPrefix": "id",
    "extendedRequestId": "request-id",
    "httpMethod": "GET",
    "identity": {
      "accessKey": null,
      "accountId": null,
      "caller": null,
      "cognitoAuthenticationProvider": null,
      "cognitoAuthenticationType": null,
      "cognitoIdentityId": null,
      "cognitoIdentityPoolId": null,
      "principalOrgId": null,
      "sourceIp": "IP",
      "user": null,
      "userAgent": "user-agent",
      "userArn": null,
      "clientCert": {
        "clientCertPem": "CERT_CONTENT",
        "subjectDN": "www.example.com",
        "issuerDN": "Example issuer",
        "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1",
        "validity": {
          "notBefore": "May 28 12:30:02 2019 GMT",
          "notAfter": "Aug  5 09:36:04 2021 GMT"
        }
      }
    },
    "path": "/my/path",
    "protocol": "HTTP/1.1",
    "requestId": "id=",
    "requestTime": "04/Mar/2020:19:15:17 +0000",
    "requestTimeEpoch": 1583349317135,
    "resourceId": null,
    "resourcePath": "/my/path",
    "stage": "$default"
  },
  "pathParameters": null,
  "stageVariables": null,
  "body": "Hello from Lambda!",
  "isBase64Encoded": false
}
```

**注意**  
在输入中：  
`headers` 密钥只能包含单值标头。
`multiValueHeaders` 密钥可以包含多值标头以及单值标头。
如果您指定 `headers` 和 `multiValueHeaders` 的值，API Gateway 会将它们合并为一个列表。如果两者都指定了相同的键/值对，则合并列表中只会出现 `multiValueHeaders` 的值。

在后端 Lambda 函数的输入中，`requestContext` 对象是键/值对的映射。在每对中，键为 [$context](api-gateway-mapping-template-reference.md#context-variable-reference) 变量属性的名称，值为该属性的值。API Gateway 可能会向映射中添加新键。

根据启用的功能，不同 API 的 `requestContext` 映射可能有所不同。例如，在上述示例中，未指定任何授权类型，因此不存在任何 `$context.authorizer.*` 或 `$context.identity.*` 属性。当指定某个授权类型时，这会导致 API Gateway 将授权用户信息传递给 `requestContext.identity` 对象中的集成端点，如下所示：
+ 当授权类型为 `AWS_IAM` 时，授权用户信息包括 `$context.identity.*` 属性。
+ 当授权类型为 `COGNITO_USER_POOLS`（Amazon Cognito 授权方）时，授权用户信息包括 `$context.identity.cognito*` 和 `$context.authorizer.claims.*`。
+ 当授权类型为 `CUSTOM`（Lambda 授权方）时，授权用户信息包括 `$context.authorizer.principalId` 及其他适用的 `$context.authorizer.*` 属性。

## 用于代理集成的 Lambda 函数的输出格式
<a name="api-gateway-simple-proxy-for-lambda-output-format"></a>

在 Lambda 代理集成中，API Gateway 需要后端 Lambda 函数根据以下 JSON 格式返回输出：

```
{
    "isBase64Encoded": {{true|false}},
    "statusCode": {{httpStatusCode}},
    "headers": { "{{headerName}}": "{{headerValue}}", ... },
    "multiValueHeaders": { "{{headerName}}": ["{{headerValue}}", "{{headerValue2}}", ...], ... },
    "body": "{{...}}"
}
```

在输出中：
+ 如果不返回任何额外的响应标头，则可以不指定 `headers` 和 `multiValueHeaders` 键。
+ `headers` 密钥只能包含单值标头。
+ `multiValueHeaders` 密钥可以包含多值标头以及单值标头。您可以使用 `multiValueHeaders` 密钥来指定所有额外的标头，包括任何单值标头。
+ 如果您指定 `headers` 和 `multiValueHeaders` 的值，API Gateway 会将它们合并为一个列表。如果两者都指定了相同的键/值对，则合并列表中只会出现 `multiValueHeaders` 的值。

要为 Lambda 代理集成启用 CORS，您必须将 `Access-Control-Allow-Origin:{{domain-name}}` 添加到输出 `headers`。`{{domain-name}}` 可以为 `*`，表示任意域名。输出 `body` 作为方法响应负载封送到前端。如果 `body` 是二进制 blob，您可以通过将 `isBase64Encoded` 设置为 `true` 并将 `*/*` 配置为**二进制媒体类型**来将其编码为采用 Base64 编码的字符串。否则，您可以将其设置为 `false` 或不进行指定。

**注意**  
有关启用二进制文件支持的更多信息，请参阅[使用 API Gateway 控制台启用二进制支持](api-gateway-payload-encodings-configure-with-console.md)。有关示例 Lambda 函数，请参阅 [从 API Gateway 中的 Lambda 代理集成返回二进制媒体](lambda-proxy-binary-media.md)。

如果函数输出属于其他格式，则 API Gateway 将返回 `502 Bad Gateway` 错误响应。

要在 Node.js 的 Lambda 函数中返回响应，您可以使用如下所示的命令：
+ 要返回成功的结果，请调用 `callback(null, {"statusCode": 200, "body": "results"})`。
+ 要引发异常，请调用 `callback(new Error('internal server error'))`。
+ 对于客户端错误（例如，如果缺少必需参数），可以调用 `callback(null, {"statusCode": 400, "body": "Missing parameters of ..."})` 以返回错误而不引发异常。

在 Node.js 的 Lambda `async` 函数中，等效的语法为：
+ 要返回成功的结果，请调用 `return {"statusCode": 200, "body": "results"}`。
+ 要引发异常，请调用 `throw new Error("internal server error")`。
+ 对于客户端错误（例如，如果缺少必需参数），可以调用 `return {"statusCode": 400, "body": "Missing parameters of ..."}` 以返回错误而不引发异常。