Amazon API Gateway
开发人员指南
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 Amazon AWS 入门

设置 API Gateway 中的 Lambda 代理集成

了解 API Gateway Lambda 代理集成

API Gateway 的 Lambda 代理集成是通过设置单个 API 方法来构建 API 的简单、强大且灵活的机制。Lambda 代理集成允许客户端调用后端的单个 Lambda 函数。该函数访问其他 AWS 服务的许多资源或功能,包括调用其他 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 代理集成的更多信息,请参阅设置具有代理资源的代理集成

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

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

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

要详细了解如何使用 API Gateway 控制台来配置与 Lambda 代理集成的 代理资源,请参阅 使用 Lambda 代理集成构建 API Gateway API

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

{ "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 表。

使用 AWS CLI 设置 Lambda 代理集成

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

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

exports.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.headers && event.headers.greeter && event.headers.greeter != "") { greeter = event.headers.greeter; } res.body = "Hello, " + greeter + "!"; callback(null, res); };

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

使用 AWS CLI 设置 Lambda 代理集成

  1. 调用 create-rest-api 命令以创建 API:

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

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

    { "name": "HelloWorldProxy (AWS 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 以使用名为 HelloWorld 的 Lambda 函数设置 ANY /{proxy+} 方法的集成。在提供 greeter 参数时,此函数使用消息 "Hello, {name}!" 响应请求,在未设置查询字符串参数时使用 "Hello, World!" 响应。

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

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

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

    { "passthroughBehavior": "WHEN_NO_MATCH", "cacheKeyParameters": [], "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1: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
  7. 在终端中使用以下 cURL 命令测试 API。

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

    curl -X GET 'https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/greeting?greeter=jane' \ -H 'authorization: AWS4-HMAC-SHA256 Credential={access_key}/20171020/us-west-2/execute-api/aws4_request, \ SignedHeaders=content-type;host;x-amz-date, Signature=f327...5751'

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

    curl -X GET https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/hi \ -H 'authorization: AWS4-HMAC-SHA256 Credential={access_key}/20171020/us-west-2/execute-api/aws4_request, \ SignedHeaders=content-type;host;x-amz-date, Signature=f327...5751' \ -H 'content-type: application/json' \ -H 'greeter: jane'

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

    curl -X POST https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test \ -H 'authorization: AWS4-HMAC-SHA256 Credential={access_key}/20171020/us-west-2/execute-api/aws4_request, \ SignedHeaders=content-type;host;x-amz-date, Signature=f327...5751' -H 'content-type: application/json' \ -d '{ "greeter": "jane" }'

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

    Hello, jane!

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

使用 Lambda 代理集成,API Gateway 可以将整个客户端请求映射到后端 Lambda 函数的输入 event 参数,如下所示:

{ "resource": "Resource path", "path": "Path parameter", "httpMethod": "Incoming request's method name" "headers": {Incoming request headers} "queryStringParameters": {query string parameters } "pathParameters": {path parameters} "stageVariables": {Applicable stage variables} "requestContext": {Request context, including authorizer-returned key-value pairs} "body": "A JSON string of the request payload." "isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode" }

我们使用以下 POST 请求进行说明,其显示的 API 部署到 testStage,阶段变量为 stageVariableName=stageVariableValue

POST /testStage/hello/world?name=me HTTP/1.1 Host: gy415nuibc.execute-api.us-east-1.amazonaws.com Content-Type: application/json headerName: headerValue { "a": 1 }

此请求生成以下响应负载,包含从后端 Lambda 函数返回的输出,其中 input 设置为 Lambda 函数的 event 参数。

{ "message": "Hello me!", "input": { "resource": "/{proxy+}", "path": "/hello/world", "httpMethod": "POST", "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "cache-control": "no-cache", "CloudFront-Forwarded-Proto": "https", "CloudFront-Is-Desktop-Viewer": "true", "CloudFront-Is-Mobile-Viewer": "false", "CloudFront-Is-SmartTV-Viewer": "false", "CloudFront-Is-Tablet-Viewer": "false", "CloudFront-Viewer-Country": "US", "Content-Type": "application/json", "headerName": "headerValue", "Host": "gy415nuibc.execute-api.us-east-1.amazonaws.com", "Postman-Token": "9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f", "User-Agent": "PostmanRuntime/2.4.5", "Via": "1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A==", "X-Forwarded-For": "54.240.196.186, 54.182.214.83", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "queryStringParameters": { "name": "me" }, "pathParameters": { "proxy": "hello/world" }, "stageVariables": { "stageVariableName": "stageVariableValue" }, "requestContext": { "accountId": "12345678912", "resourceId": "roq9wj", "stage": "testStage", "requestId": "deef4878-7910-11e6-8f14-25afc3e9ae33", "identity": { "cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "apiKey": null, "sourceIp": "192.168.196.186", "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "PostmanRuntime/2.4.5", "user": null }, "resourcePath": "/{proxy+}", "httpMethod": "POST", "apiId": "gy415nuibc" }, "body": "{\r\n\t\"a\": 1\r\n}", "isBase64Encoded": false } }

在 Lambda 的输入中,requestContext 对象是键-值对的映射。键是 $context 变量的属性名称,值是对应的 $context 变量的属性值。API Gateway 可能会向映射添加新键。根据启用的功能,不同 API 的 requestContext 映射可能有所不同。例如,在上述示例中,$context.authorizer.* 属性不存在,因为 API 没有启用自定义授权方。

注意

处理与 Lambda 代理集成或 HTTP 代理集成结合使用的方法时,API Gateway 存在某些限制。有关详细信息,请参阅 已知问题

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

使用 Lambda 代理集成,API Gateway 会要求后端 Lambda 函数按照以下 JSON 格式返回输出:

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

在输出中,如果不返回任何额外的响应标头,则可以不指定 headers。要为 Lambda 代理集成启用 CORS,您必须将 Access-Control-Allow-Origin:domain-name 添加到输出 headersdomain-name 可以为 *,表示任意域名。输出 body 作为方法响应负载封送到前端。如果 body 是二进制 Blob,则可以将其编码为 Base64 编码的字符串并将 isBase64Encoded 设置为 true。否则,您可以将其设置为 false 或不进行指定。

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