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

使用自定义 Lambda 集成构建 API Gateway API

注意

Lambda 自定义集成,以前称为 Lambda 集成,是一项传统技术。建议您将 Lambda 代理集成用于所有新 API。有关更多信息,请参阅 使用 Lambda 代理集成构建 API Gateway API

在本演练中,我们使用 API Gateway 控制台构建一个 API,该 API 允许客户端通过 Lambda 自定义集成来调用 Lambda 函数。有关 AWS Lambda 和 Lambda 函数的更多信息,请参阅 AWS Lambda Developer Guide

为了便于学习,我们选择了一个所需 API 设置最少的简单 Lambda 函数,逐步指导您完成使用 Lambda 自定义集成构建 API Gateway API 的步骤。必要时,我们会介绍一些逻辑。有关 Lambda 自定义集成的更详细示例,请参阅 为 AWS Lambda 函数创建 API Gateway API

在创建 API 之前,通过在 AWS Lambda 中创建 Lambda 函数来设置 Lambda 后端,如下所述。

为 Lambda 自定义集成创建 Lambda 函数

注意

创建 Lambda 函数可能会导致向您的 AWS 账户收取费用。

在此步骤中,您将为Lambda 自定义集成创建一个类似“Hello, World!”的 Lambda 函数。在本演练中,该函数称为 GetStartedLambdaIntegration。它类似于我们为 Lambda 代理集成创建的函数 GetStartedLambdaProxyIntegration

GetStartedLambdaIntegration Lambda 函数的 Node.js 实现如下所示:

'use strict'; var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; var times = ['morning', 'afternoon', 'evening', 'night', 'day']; console.log('Loading function'); exports.handler = function(event, context, callback) { // Parse the input for the name, city, time and day property values let name = event.name === undefined ? 'you' : event.name; let city = event.city === undefined ? 'World' : event.city; let time = times.indexOf(event.time)<0 ? 'day' : event.time; let day = days.indexOf(event.day)<0 ? null : event.day; // Generate a greeting let greeting = 'Good ' + time + ', ' + name + ' of ' + city + '. '; if (day) greeting += 'Happy ' + day + '!'; // Log the greeting to CloudWatch console.log('Hello: ', greeting); // Return a greeting to the caller callback(null, { "greeting": greeting }); };

对于 Lambda 自定义集成,API Gateway 会将来自客户端的 Lambda 函数的输入作为集成请求正文来传递。Lambda 函数处理程序的 event 对象是输入。

我们的 Lambda 函数很简单。它会解析输入 event 对象的 namecitytimeday 属性。然后,它会以 {"message":greeting} 的 JSON 对象的形式向调用方返回问候语。该消息采用 "Good [morning|afternoon|day], [name|you] in [city|World]. Happy day!" 模式。假设 Lambda 函数的输入属于以下 JSON 对象:

{ "city": "...", "time": "...", "day": "...", "name" : "..." }

有关更多信息,请参阅 AWS Lambda Developer Guide

此外,该函数通过调用 console.log(...) 将其执行记录到 Amazon CloudWatch。这有助于在调试函数时跟踪调用。要允许 GetStartedLambdaIntegration 函数记录调用,请用用于 Lambda 函数的适当策略来设置 IAM 角色,以创建 CloudWatch 流并向流中添加日志条目。Lambda 控制台将指导您创建所需的 IAM 角色和策略。

如果您设置 API 时未使用 API Gateway 控制台 (例如,在从 Swagger 导入 API 时),则必须显式创建 (如有必要) 并设置调用角色和策略,以便 API Gateway 调用 Lambda 函数。有关如何为 API Gateway API 设置 Lambda 调用和执行角色的更多信息,请参阅 使用 IAM 许可控制对 API 的访问

与用于 Lambda 代理集成的 Lambda 函数 GetStartedLambdaProxyIntegation 相比,用于 Lambda 自定义集成的 GetStartedLambdaIntegration Lambda 函数仅从 API Gateway API 集成请求正文中获取输入。该函数可以返回任何 JSON 对象、字符串、数字、布尔值甚至是二进制 blob 形式的输出。相比而言,用于 Lambda 代理集成的 Lambda 函数可从任何请求数据获取输入,但必须返回特定 JSON 对象形式的输出。用于 Lambda 自定义集成的 GetStartedLambdaIntegration 函数可以使用 API 请求参数作为输入,前提是 API Gateway 在将客户端请求转发至后端之前,将所需的 API 请求参数映射至集成请求正文。要实现这一点,API 开发人员必须在创建 API 时创建一个映射模板并在 API 方法中对其进行配置。

现在,创建 GetStartedLambdaIntegration Lambda 函数。

创建用于 Lambda 自定义集成的 GetStartedLambdaIntegration Lambda 函数

  1. 通过以下网址打开 AWS Lambda 控制台:https://console.amazonaws.cn/lambda/

  2. 执行以下任一操作:

    • 如果显示欢迎页面,请选择 Get Started Now,然后选择 Create a function

    • 如果显示 Lambda > Functions 列表页面,请选择 Create a function

  3. Select blueprint 中,选择 Author from scratch

  4. Configure triggers 窗格中,选择 Next

  5. Configure function 窗格中,执行以下操作:

    1. Basic information 下:

      • 对于 Name,键入 GetStartedLambdaIntegration 作为 Lambda 函数名称。

      • 对于 Description,键入 Backend for the Getting Started walkthrough with Lambda custom integration。这是可选项,可将其留空。

      • 对于 Runtime,选择 Node.js 6.10

    2. Lambda function code 下:

      • 选择 Edit code inline,如果尚未显示,则在 Content entry type 下:

      • 复制本节开头部分列出的 Lambda 函数代码并将其粘贴到内联代码编辑器中。

      • 对本部分中的所有其他字段保留默认选择。

    3. Lambda function handler and role 下:

      • Handler 保留默认的 index.handler

      • 对于 Role,选择 Create new role from template(s)

      • 对于 Role name,键入一个角色名称 (例如 GetStartedLambdaIntegrationRole)。

      • 对于 Policy templates,选择 Simple Microservice permissions

        提示

        要使用现有 IAM 角色,请为 Role 选择 Choose an existing role,然后从现有角色的下拉列表中选择一个条目。或者,要创建自定义角色,请选择 Create a Custom Role 并按照说明进行操作。

    4. 对于 Tags,将其留空。

    5. 对于 Advanced settings,保留默认值。

    6. 选择 Next

    7. 选择 Create function。请记下您创建此函数所在的 AWS 区域。稍后您将需要用到它。

  6. 要测试新创建的函数,最好的做法是选择 Actions,然后选择 Configure test event

    1. 对于 Input test event,使用以下内容替换所有默认代码语句,然后选择 Save and test

      { "name": "Jonny", "city": "Seattle", "time": "morning", "day": "Wednesday" }
    2. 选择 Test 以调用该函数。将显示 Execution result: succeeded 部分。展开 Detail,您会看到以下输出。

      { "greeting": "Good morning, Jonny of Seattle. Happy Wednesday!" }

      还会将输出写入到 CloudWatch 日志中。

作为同步练习,您可以使用 IAM 控制台查看 IAM 角色 (GetStartedLambdaIntegrationRole),此角色是在创建 Lambda 函数的过程中创建的。此 IAM 角色附加了两个内联策略。一个策略规定 Lambda 执行的最基本权限。它允许创建 Lambda 函数时所在区域内您账户中的任何 CloudWatch 资源调用 CloudWatch CreateLogGroup。此策略还允许创建 CloudWatch 流和为 HelloWorldForLambdaIntegration Lambda 函数记录事件。

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "arn:aws:logs:region:account-id:*" }, { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": [ "arn:aws:logs:region:account-id:log-group:/aws/lambda/GetStartedLambdaIntegration:*" ] } ] }

另一个策略文档适用于调用此示例中未使用的其他 AWS 服务。您可以暂时跳过此策略。

与 IAM 角色关联的是可信实体 lambda.amazonaws.com。下面是信任关系:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }

此信任关系与内联策略的组合,使得 Lambda 函数能够调用 console.log() 函数来将事件记录到 CloudWatch Logs。

如果您未使用 AWS 管理控制台创建 Lambda 函数,则需按照以下示例创建所需的 IAM 角色和策略,然后手动将该角色附加到您的函数。

使用 Lambda 自定义集成创建 API

创建并测试 Lambda 函数 (GetStartedLambdaIntegration) 后,您便已准备就绪,可以通过 API Gateway API 来公开该函数。为了便于说明,我们用通用 HTTP 方法公开 Lambda 函数。我们使用请求正文、URL 路径变量、查询字符串和标头来接收来自客户端的所需输入数据。我们为 API 启用 API Gateway 请求验证程序,以确保正确定义并指定所有必需的数据。我们为 API Gateway 配置映射模板,以根据后端 Lambda 函数的要求将客户端提供的请求数据转换为有效格式。

该 API 名为 GetStartedLambdaIntegrationAPI

使用 Lambda 函数创建包含 Lambda 自定义集成的 API

  1. 启动 API Gateway 控制台。

  2. 选择 Create new API

    1. API name 键入 GetStartedLambdaIntegrationAPI

    2. Description 键入 API 的描述或将其留空。

    3. 选择 Create API

  3. Resources 下选择根资源 (/)。从 Actions 菜单中,选择 Create Resource

    1. Resource Name 键入 city

    2. Resource Path 替换为 {city}。这是用于从客户端获取输入的模板化路径变量的示例。稍后,我们将介绍如何使用映射模板将此路径变量映射为 LAM 函数输入。

    3. 选择 Enable API Gateway Cors 选项。

    4. 选择 Create Resource

  4. 突出显示新创建的 /{city} 资源,然后从 Actions 中选择 Create Method

    1. 从 HTTP 方法下拉菜单中选择 ANYANY HTTP 动词是客户端在运行时提交的有效 HTTP 方法的占位符。此示例显示,ANY 方法可用于 Lambda 自定义集成和 Lambda 代理集成。

    2. 要保存设置,请选择对勾图标。

  5. Method Execution 中,对于 ANY /{city} 方法,执行以下操作:

    1. Integration type 选择 Lambda Function

    2. Use Lambda Proxy integration 框清除,以使用自定义 Lambda 自定义集成。

    3. 选择您创建 Lambda 函数时所在的区域;例如,us-west-2

    4. Lambda Function 中键入 Lambda 函数的名称;例如,GetStartedLambdaIntegration

    5. 选择 Save

    6. Add Permission to Lambda Function 中选择 OK,让 API Gateway 设置 API 调用集成的 Lambda 函数所需的访问权限。

  6. Method Execution 中,选择 Method Request 并进行如下配置:

    • 查询字符串参数 (time)

    • 设置标头参数 (day)

    • 定义负载属性 (callerName)

    运行时,客户端可以使用这些请求参数和请求正文来提供一天中的某个时间、一周中的某天以及调用方的名称。您已经配置了 /{city} 路径变量。

    1. Settings 下,选择铅笔图标,以从 Request Validator 下拉菜单中选择 Validate body, query string parameters, and headers。这样 API Gateway 便可在将请求转发给 Lambda 函数之前执行基本请求验证。

    2. 展开 URL Query String Parameters 部分。选择 Add query string。为 Name 键入 time。选择 Required 选项,并选择复选标记以保存设置。清除 Caching 以避免此练习产生不必要的费用。

    3. 展开 HTTP Request Headers 部分。选择 Add header。为 Name 键入 day。选择 Required 选项,并选择复选标记以保存设置。清除 Caching 以避免此练习产生不必要的费用。

    4. 要定义方法请求负载,请执行以下操作:

      1. 要定义模型,请在 API Gateway 主导航窗格中的 API 下选择 Models,然后选择 Create

      2. 对于 Model name,键入 GetStartedLambdaIntegrationUserInput

      3. Content type 键入 application/json

      4. Model description 键入描述或将其留空。

      5. 将下面的架构定义复制到 Model schema 编辑器中:

        { "$schema": "http://json-schema.org/draft-04/schema#", "title": "GetStartedLambdaIntegrationInputModel", "type": "object", "properties": { "callerName": { "type": "string" } } }
      6. 选择 Save 以完成输入模型的定义。

      7. 返回 Method Request 并展开 Request body。选择 Add model。为 Content type 键入 application/json。为 Model name 选择 GetStartedLambdaIntegrationInput。选择对勾图标以保存设置。

  7. ANY /{city} 方法的 Method Execution 中,选择 Integration Request 以设置正文映射模板。此时将根据后端 Lambda 函数的要求将先前配置的 nameQuerynameHeader 的方法请求参数映射为 JSON 负载。

    1. 展开 Body Mapping Templates 部分。选择 Add mapping template。为 Content-Type 键入 application/json。选择对勾图标以保存设置。

    2. 在 VTL 脚本编辑器中键入以下映射模板。选择 Save 以完成设置。

  8. 选择 Integration Request 以设置映射模板,来将客户端提供的请求数据转换为集成的 Lambda 函数的输入格式:

    1. 展开 Body mapping templates 部分。

    2. 对于 Request body passthrough,选中建议的 When there are no templates defined

    3. 选择 Add mapping template

    4. Content-type 键入 application/json

    5. 选择对勾图标以保存设置。

    6. Generate template 中选择 GetStartedLambaIntegrationUserInput,以生成初始映射模板。由于您已定义模型架构,因此该选项可用,如果没有模型架构,您将需要从头开始编写映射模板。

    7. 按如下所示,在映射模板编辑器中修改映射脚本:

      #set($inputRoot = $input.path('$')) { "city": "$input.params('city')", "time": "$input.params('time')", "day": "$input.params('day')", "name": "$inputRoot.callerName" }

测试 API 方法的调用

API Gateway 控制台提供了测试工具,以供您在部署 API 之前测试 API 的调用。您使用控制台的测试功能通过提交以下请求来测试 API:

POST /Seattle?time=morning day:Wednesday { "callerName": "John" }

在此测试请求中,您可以将 ANY 设置为 POST、将 {city} 设置为 Seattle、将 Wednesday 分配为 day 标头值,将 "John" 分配为 callerName 值。

测试 ANY /{city} 方法的调用

  1. Method Execution 中选择 Test

  2. Method 下拉列表中选择 POST

  3. {city} 路径变量键入 Seattle

  4. 对于 day 查询字符串参数,键入 morning

  5. 对于 Request Body,键入 { "callerName":"John" }

  6. 选择 Test

  7. 验证返回的响应负载是否如下所示:

    { "greeting": "Good morning, John of Seattle. Happy Wednesday!" }
  8. 您还可以查看日志,以确定 API Gateway 如何处理请求和响应。

    Execution log for request test-request Thu Aug 31 01:07:25 UTC 2017 : Starting execution for request: test-invoke-request Thu Aug 31 01:07:25 UTC 2017 : HTTP Method: POST, Resource Path: /Seattle Thu Aug 31 01:07:25 UTC 2017 : Method request path: {city=Seattle} Thu Aug 31 01:07:25 UTC 2017 : Method request query string: {time=morning} Thu Aug 31 01:07:25 UTC 2017 : Method request headers: {day=Wednesday} Thu Aug 31 01:07:25 UTC 2017 : Method request body before transformations: { "callerName": "John" } Thu Aug 31 01:07:25 UTC 2017 : Request validation succeeded for content type application/json Thu Aug 31 01:07:25 UTC 2017 : Endpoint request URI: https://lambda.us-west-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-west-2:123456789012:function:GetStartedLambdaIntegration/invocations Thu Aug 31 01:07:25 UTC 2017 : Endpoint request headers: {x-amzn-lambda-integration-tag=test-request, Authorization=****************************************************************************************************************************************************************************************************************************************************************************************************************************************338c72, X-Amz-Date=20170831T010725Z, x-amzn-apigateway-api-id=beags1mnid, X-Amz-Source-Arn=arn:aws:execute-api:us-west-2:123456789012:beags1mnid/null/POST/{city}, Accept=application/json, User-Agent=AmazonAPIGateway_beags1mnid, X-Amz-Security-Token=FQoDYXdzELL//////////wEaDMHGzEdEOT/VvGhabiK3AzgKrJw+3zLqJZG4PhOq12K6W21+QotY2rrZyOzqhLoiuRg3CAYNQ2eqgL5D54+63ey9bIdtwHGoyBdq8ecWxJK/YUnT2Rau0L9HCG5p7FC05h3IvwlFfvcidQNXeYvsKJTLXI05/yEnY3ttIAnpNYLOezD9Es8rBfyruHfJfOqextKlsC8DymCcqlGkig8qLKcZ0hWJWVwiPJiFgL7laabXs++ZhCa4hdZo4iqlG729DE4gaV1mJVdoAagIUwLMo+y4NxFDu0r7I0/EO5nYcCrppGVVBYiGk7H4T6sXuhTkbNNqVmXtV3ch5bOlh7 [TRUNCATED] Thu Aug 31 01:07:25 UTC 2017 : Endpoint request body after transformations: { "city": "Seattle", "time": "morning", "day": "Wednesday", "name" : "John" } Thu Aug 31 01:07:25 UTC 2017 : Sending request to https://lambda.us-west-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-west-2:123456789012:function:GetStartedLambdaIntegration/invocations Thu Aug 31 01:07:25 UTC 2017 : Received response. Integration latency: 328 ms Thu Aug 31 01:07:25 UTC 2017 : Endpoint response body before transformations: {"greeting":"Good morning, John of Seattle. Happy Wednesday!"} Thu Aug 31 01:07:25 UTC 2017 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=c0475a28-8de8-11e7-8d3f-4183da788f0f, Connection=keep-alive, Content-Length=62, Date=Thu, 31 Aug 2017 01:07:25 GMT, X-Amzn-Trace-Id=root=1-59a7614d-373151b01b0713127e646635;sampled=0, Content-Type=application/json} Thu Aug 31 01:07:25 UTC 2017 : Method response body after transformations: {"greeting":"Good morning, John of Seattle. Happy Wednesday!"} Thu Aug 31 01:07:25 UTC 2017 : Method response headers: {X-Amzn-Trace-Id=sampled=0;root=1-59a7614d-373151b01b0713127e646635, Content-Type=application/json} Thu Aug 31 01:07:25 UTC 2017 : Successfully completed execution Thu Aug 31 01:07:25 UTC 2017 : Method completed with status: 200

    日志在映射之前显示传入请求并在映射之后显示集成请求。当测试失败时,日志对于评估原始输入是否正确或映射模板工作是否正常很有用。

部署 API

测试调用是一种模拟,会受到一些限制。例如,它会绕过 API 中应用的任何授权机制。要实时测试 API 执行,您必须先部署 API。要部署 API,您需创建一个阶段,以创建当时的 API 快照。阶段名称还定义在 API 的默认主机名后面的基本路径。API 的根资源附加在阶段名称之后。当您修改 API 时,必须将其重新部署到新阶段或现有阶段,然后更改才会生效。

将 API 部署到某个阶段

  1. APIs 窗格中选择 API 或从 Resources 窗格中选择资源或方法。从 Actions 下拉菜单中选择 Deploy API

  2. 对于 Deployment stage,选择 New Stage

  3. 对于 Stage name,键入名称;例如,test

    注意

    输入必须是 UTF-8 编码 (即未本地化) 的文本。

  4. 对于 Stage description,键入描述或将其留空。

  5. 对于 Deployment description,键入描述或将其留空。

  6. 选择 Deploy。成功部署 API 后,您将看到该 API 的基本 URL (默认的主机名加上阶段名称) 在 Stage Editor 顶部显示为 Invoke URL。此基本 URL 的一般模式是 https://api-id.region.amazonaws.com/stageName。例如,在 us-west-2 区域中创建并部署到 test 阶段的 API (beags1mnid) 的基本 URL 是 https://beags1mnid.execute-api.us-west-2.amazonaws.com/test

在部署阶段测试 API

可通过若干种方法来测试已部署的 API。对于仅使用 URL 路径变量或查询字符串参数的 GET 请求,您可以在浏览器中键入 API 资源的 URL。对于其他方法,您必须使用更高级的 REST API 测试实用程序,如 POSTMANcURL

使用 cURL 测试 API

  1. 在连接到 Internet 的本地计算机上打开终端窗口。

  2. 测试 POST /Seattle?time=evening

    复制以下 cURL 命令并将其粘贴到终端窗口中。

    curl -v -X POST \ 'https://beags1mnid.execute-api.us-west-2.amazonaws.com/test/Seattle?time=evening' \ -H 'content-type: application/json' \ -H 'day: Thursday' \ -H 'x-amz-docs-region: us-west-2' \ -d '{ "callerName": "John" }'

    您应获得一个包含以下负载的成功响应:

    {"greeting":"Good evening, John of Seattle. Happy Thursday!"}

    如果您在此方法请求中将 POST 更改为 PUT,则会获得相同的响应。

  3. 测试 GET /Boston?time=morning

    复制以下 cURL 命令并将其粘贴到终端窗口中。

    curl -X GET \ 'https://beags1mnid.execute-api.us-west-2.amazonaws.com/test/Boston?time=morning' \ -H 'content-type: application/json' \ -H 'day: Thursday' \ -H 'x-amz-docs-region: us-west-2' \ -d '{ "callerName": "John" }'

    您将获得一个包含以下错误消息的 400 Bad Request 响应:

    {"message": "Invalid request body"}

    原因是您提交的 GET 请求无法获取负载并且请求验证失败。

清除

如果您不再需要您为本演练创建的 Lambda 函数,现在可以将其删除。您也可以删除附带的 IAM 资源。

警告

如果您计划完成本系列中的其他演练,请不要删除 Lambda 执行角色或 Lambda 调用角色。如果您删除您的 API 所依赖的某个 Lambda 函数,这些 API 将不再有效。Lambda 函数删除操作无法撤消。如果您要再次使用 Lambda 函数,必须重新创建该函数。

如果您删除 Lambda 函数所依赖的 IAM 资源,Lambda 函数将不再有效,依赖于此函数的 API 也不再有效。IAM 资源删除操作无法撤消。如果您要再次使用 IAM 资源,必须重新创建该资源。

删除 Lambda 函数

  1. 通过以下网址登录 AWS 管理控制台并打开 AWS Lambda 控制台:https://console.amazonaws.cn/lambda/

  2. 从函数列表中选择 GetHelloWorld,再选择 Actions,然后选择 Delete function。当系统提示时,再次选择 Delete

  3. 从函数列表中选择 GetHelloWithName,再选择 Actions,然后选择 Delete function。当系统提示时,再次选择 Delete

删除相关联的 IAM 资源

  1. 通过以下网址打开 IAM 控制台:https://console.amazonaws.cn/iam/

  2. Details 中,选择 Roles

  3. 从角色列表中选择 APIGatewayLambdaExecRole,再选择 Role Actions,然后选择 Delete Role。在系统提示时,选择 Yes, Delete

  4. Details 中,选择 Policies

  5. 从策略列表中选择 APIGatewayLambdaExecPolicy,再选择 Policy Actions,然后选择 Delete。系统提示时,选择 Delete

现在,您已经完成了本演练。