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

教程:利用 Lambda 非代理集成创建 REST API

在本演练中,我们使用 API Gateway 控制台构建一个 API,该 API 允许客户端通过 Lambda 非代理集成(又称为“自定义集成”)来调用 Lambda 函数。有关 Amazon Lambda 和 Lambda 函数的更多信息,请参阅 Amazon Lambda 开发人员指南

为了便于学习,我们选择了一个所需 API 设置最少的简单 Lambda 函数,逐步指导您完成使用 Lambda 自定义集成构建 API Gateway API 的步骤。必要时,我们会介绍一些逻辑。有关 Lambda 自定义集成的更详细示例,请参阅 教程:通过两种 Amazon 服务集成和一种 Lambda 非代理集成创建计算器 REST API

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

为 Lambda 非代理集成创建 Lambda 函数

注意

创建 Lambda 函数可能会导致您的Amazon账户产生费用。

在此步骤中,您将为 Lambda 自定义集成创建一个如“Hello, World!”的 Lambda 函数。在本演练中,该函数称为 GetStartedLambdaIntegration

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'); export const 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 }); };
Python
import json days = { 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'} times = {'morning', 'afternoon', 'evening', 'night', 'day'} def lambda_handler(event, context): print(event) # parse the input for the name, city, time, and day property values try: if event['name']: name = event['name'] except KeyError: name = 'you' try: if event['city']: city = event['city'] except KeyError: city = 'World' try: if event['time'] in times: time = event['time'] else: time = 'day' except KeyError: time = 'day' try: if event['day'] in days: day = event['day'] else: day = '' except KeyError: day = '' # Generate a greeting greeting = 'Good ' + time + ', ' + name + ' of ' + \ city + '.' + ['', ' Happy ' + day + '!'][day != ''] # Log the greeting to CloudWatch print(greeting) # Return a greeting to the caller return {"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" : "..." }

有关更多信息,请参阅 Amazon Lambda 开发人员指南

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

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

与用于 Lambda 代理集成的 Lambda 函数 GetStartedLambdaProxyIntegration 相比,用于 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. 打开 Amazon Lambda 控制台,地址:https://console.aws.amazon.com/lambda/

  2. 请执行下列操作之一:

    • 如果显示欢迎页面,请选择立即开始使用,然后选择创建函数

    • 如果显示 Lambda > 函数列表页面,请选择创建函数

  3. 选择从头开始创作

  4. 从头开始创作窗格中,执行以下操作:

    1. 名称中,输入 GetStartedLambdaIntegration 作为 Lambda 函数名称。

    2. 运行时中,选择受支持的最新 Node.jsPython 运行时。

    3. 权限下,展开更改默认执行角色。在执行角色下拉列表中,选择从 Amazon 策略模板创建新角色

    4. 对于角色名称,输入您角色的名称(例如 GetStartedLambdaIntegrationRole)。

    5. 对于策略模板,选择简单微服务权限

    6. 选择创建函数

  5. 配置函数窗格中的函数代码下,执行以下操作:

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

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

    3. 选择部署

  6. 要测试新创建的函数,请选择测试选项卡。

    1. 对于事件名称,输入 HelloWorldTest

    2. 对于事件 JSON,请将默认代码替换为以下代码。

      { "name": "Jonny", "city": "Seattle", "time": "morning", "day": "Wednesday" }
    3. 选择测试以调用该函数。将显示执行结果: 成功部分。展开详细信息,您会看到以下输出。

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

      还会将输出写入 CloudWatch Logs 中。

作为同步练习,您可以使用 IAM 控制台查看 IAM 角色 (GetStartedLambdaIntegrationRole),此角色是在创建 Lambda 函数的过程中创建的。此 IAM 角色附加了两个内联策略。一个策略规定 Lambda 执行的最基本权限。它允许在创建 Lambda 函数的区域中为您账户的任何 CloudWatch 资源调用 CloudWatch CreateLogGroup。此策略还允许创建 CloudWatch 流和为 GetStartedLambdaIntegration 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:*" ] } ] }

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

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

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

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

使用 Lambda 非代理集成创建 API

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

使用 Lambda 非代理集成创建 API
  1. 通过以下网址登录到 API Gateway 控制台:https://console.aws.amazon.com/apigateway

  2. 如果您是第一次使用 API Gateway,您会看到一个介绍服务特征的页面。在 REST API 下,选择生成。当创建示例 API 弹出框出现时,选择确定

    如果这不是您首次使用 API Gateway,请选择创建 API。在 REST API 下,选择生成

  3. 对于 API 名称,请输入 LambdaNonProxyAPI

  4. (可选)对于描述,输入描述。

  5. API 端点类型设置保留为区域

  6. 选择创建 API

创建 API 后,您将创建一个 /{city} 资源。这是带有从客户端获取输入的路径变量的资源示例。稍后,您将使用映射模板将此路径变量映射到 Lambda 函数输入。

创建资源
  1. 选择创建资源

  2. 代理资源保持为关闭状态。

  3. 资源路径保持为 /

  4. 对于资源名称,输入 {city}

  5. CORS(跨源资源共享)保持为关闭状态。

  6. 选择创建资源

创建 /{city} 资源后,您将创建一个 ANY 方法。ANY HTTP 动词是客户端在运行时提交的有效 HTTP 方法的占位符。此示例显示,ANY 方法可用于 Lambda 自定义集成和 Lambda 代理集成。

创建 ANY 方法
  1. 选择 /{city} 资源,然后选择创建方法

  2. 对于方法类型,选择 ANY

  3. 对于集成类型,选择 Lambda 函数

  4. 保持 Lambda 代理集成处于关闭状态。

  5. 对于 Lambda 函数,选择您创建 Lambda 函数的 Amazon Web Services 区域,然后输入函数名称。

  6. 选择方法请求设置

    现在,您可以为 URL 路径变量、查询字符串参数和标头开启请求验证程序,来确保定义了所有必需的数据。在本示例中,您将创建一个 time 查询字符串参数和一个 day 标头。

  7. 对于请求验证程序,选择验证查询字符串参数和标头

  8. 选择 URL 查询字符串参数并执行以下操作:

    1. 选择添加查询字符串

    2. 名称中,输入 time

    3. 打开必需

    4. 缓存保持为关闭状态。

  9. 选择 HTTP 请求标头并执行以下操作:

    1. 选择添加标头

    2. 名称中,输入 day

    3. 打开必需

    4. 缓存保持为关闭状态。

  10. 选择创建方法

打开请求验证程序后,您可以根据后端 Lambda 函数的要求,通过添加正文映射模板来配置 ANY 方法的集成请求,以将传入的请求转换为 JSON 负载。

配置集成请求
  1. 集成请求选项卡的集成请求设置下,选择编辑

  2. 对于请求正文传递,选择当未定义模板时(推荐)

  3. 选择映射模板

  4. 选择添加映射模板

  5. 对于内容类型,输入 application/json

  6. 对于模板正文,输入以下代码:

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

测试 API 方法的调用

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

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

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

测试 ANY 方法
  1. 选择测试选项卡。您可能需要选择右箭头按钮,以显示该选项卡。

  2. 对于方法类型,选择 POST

  3. 对于路径,在城市下输入 Seattle

  4. 对于查询字符串,输入 time=morning

  5. 对于标头,输入 day:Wednesday

  6. 对于请求正文,输入 { "callerName": "John" }

  7. 选择测试

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

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

您还可以查看日志,以确定 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. 选择部署 API

  2. 对于阶段,选择新建阶段

  3. 对于阶段名称,输入 test

    注意

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

  4. (可选)对于描述,输入描述。

  5. 选择部署

阶段详细信息下,选择复制图标以复制您 API 的调用 URL。API 的基本 URL 的一般模式是 https://api-id.region.amazonaws.com/stageName。例如,在 beags1mnid 区域中创建并部署到 us-west-2 阶段的 API (test) 的基本 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,则会获得相同的响应。

清除

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

警告

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

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

删除 Lambda 函数
  1. 登录到 Amazon Web Services Management Console,然后通过以下网址打开 Amazon Lambda 控制台:https://console.aws.amazon.com/lambda/

  2. 从函数列表中选择 GetStartedLambdaIntegration,再选择操作,然后选择删除函数。当系统提示时,再次选择删除

删除相关联的 IAM 资源
  1. 通过以下网址打开 IAM 控制台:https://console.aws.amazon.com/iam/

  2. 详细信息中,选择角色

  3. 从角色列表中选择 GetStartedLambdaIntegrationRole,再选择角色操作,然后选择删除角色。按照控制台中的步骤删除该角色。