在 API Gateway 中设置 Lambda 代理集成 - Amazon API Gateway
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

在 API Gateway 中设置 Lambda 代理集成

了解 API Gateway Lambda 代理集成

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

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

因为 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 支持多个具有相同名称的标头和查询字符串参数。多值标头以及单值标头和参数可以组合使用相同的请求和响应。有关更多信息,请参阅 用于代理集成的 Lambda 函数的输入格式用于代理集成的 Lambda 函数的输出格式

设置具有 Lambda 代理集成的代理资源

要设置具有 Lambda 代理集成类型的代理资源,请创建一个具有“贪婪”路径参数(例如,/parent/{proxy+})的 API 资源,并将该资源与 arn:aws:lambda:us-west-2:123456789012:function:SimpleLambda4ProxyResource 方法上的 Lambda 函数后端(例如,ANY)集成。“贪婪”路径参数必须位于 API 资源路径的末尾。与处理非代理资源一样,您可以通过使用 API Gateway 控制台、导入 OpenAPI 定义文件或直接调用 API Gateway REST API 来设置代理资源。

以下 OpenAPI API 定义文件显示了一个 API 的示例,该 API 具有与名为 SimpleLambda4ProxyResource 的 Lambda 函数集成的代理资源。

OpenAPI 3.0
{ "openapi": "3.0.0", "info": { "version": "2016-09-12T17:50:37Z", "title": "ProxyIntegrationWithLambda" }, "paths": { "/{proxy+}": { "x-amazon-apigateway-any-method": { "parameters": [ { "name": "proxy", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": {}, "x-amazon-apigateway-integration": { "responses": { "default": { "statusCode": "200" } }, "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:SimpleLambda4ProxyResource/invocations", "passthroughBehavior": "when_no_match", "httpMethod": "POST", "cacheNamespace": "roq9wj", "cacheKeyParameters": [ "method.request.path.proxy" ], "type": "aws_proxy" } } } }, "servers": [ { "url": "https://gy415nuibc.execute-api.us-east-1.amazonaws.com/{basePath}", "variables": { "basePath": { "default": "/testStage" } } } ] }
OpenAPI 2.0
{ "swagger": "2.0", "info": { "version": "2016-09-12T17:50:37Z", "title": "ProxyIntegrationWithLambda" }, "host": "gy415nuibc.execute-api.us-east-1.amazonaws.com", "basePath": "/testStage", "schemes": [ "https" ], "paths": { "/{proxy+}": { "x-amazon-apigateway-any-method": { "produces": [ "application/json" ], "parameters": [ { "name": "proxy", "in": "path", "required": true, "type": "string" } ], "responses": {}, "x-amazon-apigateway-integration": { "responses": { "default": { "statusCode": "200" } }, "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:SimpleLambda4ProxyResource/invocations", "passthroughBehavior": "when_no_match", "httpMethod": "POST", "cacheNamespace": "roq9wj", "cacheKeyParameters": [ "method.request.path.proxy" ], "type": "aws_proxy" } } } } }

在 Lambda 代理集成中,在运行时,API Gateway 将传入请求映射到 Lambda 函数的输入 event 参数中。该输入包含请求方法、路径、标头、任何字符串参数、任何负载、关联的上下文以及定义的任何阶段变量。输入格式在用于代理集成的 Lambda 函数的输入格式中说明。为了让 API Gateway 将 Lambda 输出成功映射到 HTTP 响应,Lambda 函数必须按照用于代理集成的 Lambda 函数的输出格式中说明的格式输出结果。

在通过 ANY 方法实现代理资源的 Lambda 代理集成的过程中,单个后端 Lambda 函数充当通过代理资源的所有请求的事件处理程序。例如,要记录流量模式,您可以让移动设备提交一个代理资源的 URL 路径中附带 /state/city/street/house 的请求,从而发送包含州、城市、街道和建筑信息的位置信息。然后,后端 Lambda 函数可以分析 URL 路径并将位置元组插入 DynamoDB 表。

使用 Amazon CLI 设置 Lambda 代理集成

在本部分中,我们将演示如何使用 Amazon CLI 设置 API 与 Lambda 代理集成。

注意

有关使用 API Gateway 控制台配置与 Lambda 代理集成的代理资源的详细说明,请参阅 教程:使用 Lambda 代理集成构建 Hello World REST API

例如,我们使用以下示例 Lambda 函数作为 API 的后端:

export const handler = function(event, context, callback) { console.log('Received event:', JSON.stringify(event, null, 2)); var res ={ "statusCode": 200, "headers": { "Content-Type": "*/*" } }; var greeter = 'World'; if (event.greeter && event.greeter!=="") { greeter = event.greeter; } else if (event.body && event.body !== "") { var body = JSON.parse(event.body); if (body.greeter && body.greeter !== "") { greeter = body.greeter; } } else if (event.queryStringParameters && event.queryStringParameters.greeter && event.queryStringParameters.greeter !== "") { greeter = event.queryStringParameters.greeter; } else if (event.multiValueHeaders && event.multiValueHeaders.greeter && event.multiValueHeaders.greeter != "") { greeter = event.multiValueHeaders.greeter.join(" and "); } else if (event.headers && event.headers.greeter && event.headers.greeter != "") { greeter = event.headers.greeter; } res.body = "Hello, " + greeter + "!"; callback(null, res); };

将这一点与 Lambda 自定义集成设置对比,对此 Lambda 函数的输入可能出现在请求参数和正文中。您可以有更多的自主度来允许客户端传递相同的输入数据。在这里,客户端可以将欢迎者的姓名作为查询字符串参数、标题或正文属性传递。该函数还支持 Lambda 自定义集成。API 设置更简单。您完全无需配置方法响应或集成响应。

使用 Amazon CLI 设置 Lambda 代理集成
  1. 调用 create-rest-api 命令以创建 API:

    aws apigateway create-rest-api --name 'HelloWorld (Amazon CLI)' --region us-west-2

    请注意在响应中生成的 API 的 id 值 (te6si5ach7)。

    { "name": "HelloWorldProxy (Amazon CLI)", "id": "te6si5ach7", "createdDate": 1508461860 }

    您在此部分中需要 API id

  2. 调用 get-resources 命令以获取根资源 id

    aws apigateway get-resources --rest-api-id te6si5ach7 --region us-west-2

    成功的响应如下所示:

    { "items": [ { "path": "/", "id": "krznpq9xpg" } ] }

    记录根资源 id 值 (krznpq9xpg)。您在下一个步骤中和以后需要用到它。

  3. 调用 create-resource 以创建 API Gateway 资源 /greeting

    aws apigateway create-resource --rest-api-id te6si5ach7 \ --region us-west-2 \ --parent-id krznpq9xpg \ --path-part {proxy+}

    成功响应的形式与下方类似:

    { "path": "/{proxy+}", "pathPart": "{proxy+}", "id": "2jf6xt", "parentId": "krznpq9xpg" }

    记录生成的 {proxy+} 资源的 id 值 (2jf6xt)。在下一个步骤中,您需要它在 /{proxy+} 资源上创建方法。

  4. 调用 put-method 以创建 ANY 方法的 ANY /{proxy+} 请求:

    aws apigateway put-method --rest-api-id te6si5ach7 \ --region us-west-2 \ --resource-id 2jf6xt \ --http-method ANY \ --authorization-type "NONE"

    成功响应的形式与下方类似:

    { "apiKeyRequired": false, "httpMethod": "ANY", "authorizationType": "NONE" }

    此 API 方法允许客户端从后端的 Lambda 函数接收或发送问候语。

  5. 调用 put-integration 以使用名为 ANY /{proxy+} 的 Lambda 函数设置 HelloWorld 方法的集成。在提供 greeter 参数时,此函数使用消息 "Hello, {name}!" 响应请求,在未设置查询字符串参数时使用 "Hello, World!" 响应。

    aws apigateway put-integration \ --region us-west-2 \ --rest-api-id te6si5ach7 \ --resource-id 2jf6xt \ --http-method ANY \ --type AWS_PROXY \ --integration-http-method POST \ --uri arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:123456789012:function:HelloWorld/invocations \ --credentials arn:aws:iam::123456789012:role/apigAwsProxyRole
    重要

    对于 Lambda 集成,根据函数调用的 Lambda 服务操作规范,您必须为集成请求使用 HTTP 方法 POSTapigAwsProxyRole 的 IAM 角色必须具有策略,允许 apigateway 服务调用 Lambda 函数。有关 IAM 权限的更多信息,请参阅 用于调用 API 的 API Gateway 权限模型

    成功输出的形式与下方类似:

    { "passthroughBehavior": "WHEN_NO_MATCH", "cacheKeyParameters": [], "uri": "arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:1234567890:function:HelloWorld/invocations", "httpMethod": "POST", "cacheNamespace": "vvom7n", "credentials": "arn:aws:iam::1234567890:role/apigAwsProxyRole", "type": "AWS_PROXY" }

    不同于为 credentials 提供 IAM 角色,您可以调用 add-permission 命令以添加基于资源的权限。这是 API Gateway 控制台的功能。

  6. 调用 create-deployment 以部署 API 到 test 阶段:

    aws apigateway create-deployment --rest-api-id te6si5ach7 --stage-name test --region us-west-2
  7. 在终端中使用以下 cURL 命令测试 API。

    使用查询字符串参数 ?greeter=jane 调用 API:

    curl -X GET 'https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/greeting?greeter=jane'

    使用 greeter:jane 的标头参数调用 API:

    curl -X GET https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/hi \ -H 'content-type: application/json' \ -H 'greeter: jane'

    使用 {"greeter":"jane"} 的正文调用 API:

    curl -X POST https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/hi \ -H 'content-type: application/json' \ -d '{ "greeter": "jane" }'

    在所有情况下,输出为具有以下响应正文的 200 响应:

    Hello, jane!

用于代理集成的 Lambda 函数的输入格式

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

{ "resource": "/my/path", "path": "/my/path", "httpMethod": "GET", "headers": { "header1": "value1", "header2": "value1,value2" }, "multiValueHeaders": { "header1": [ "value1" ], "header2": [ "value1", "value2" ] }, "queryStringParameters": { "parameter1": "value1,value2", "parameter2": "value" }, "multiValueQueryStringParameters": { "parameter1": [ "value1", "value2" ], "parameter2": [ "value" ] }, "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 密钥可以包含多值标头以及单值标头。

  • 如果您指定 headersmultiValueHeaders 的值,API Gateway 会将它们合并为一个列表。如果两者都指定了相同的键/值对,则合并列表中只会出现 multiValueHeaders 的值。

在后端 Lambda 函数的输入中,requestContext 对象是键/值对的映射。在每对中,键为 $context 变量属性的名称,值为该属性的值。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 函数的输出格式

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

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

在输出中:

  • 如果不返回任何额外的响应标头,则可以不指定 headersmultiValueHeaders 键。

  • headers 密钥只能包含单值标头。

  • multiValueHeaders 密钥可以包含多值标头以及单值标头。您可以使用 multiValueHeaders 密钥来指定所有额外的标头,包括任何单值标头。

  • 如果您指定 headersmultiValueHeaders 的值,API Gateway 会将它们合并为一个列表。如果两者都指定了相同的键/值对,则合并列表中只会出现 multiValueHeaders 的值。

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

注意

有关启用二进制文件支持的更多信息,请参阅使用 API Gateway 控制台启用二进制支持。有关示例 Lambda 函数,请参阅 从 Lambda 代理集成返回二进制媒体

如果函数输出属于其他格式,则 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 ..."} 以返回错误而不引发异常。