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

教程:使用 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 非代理集成创建 Calc REST API

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

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

注意

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

在此步骤中,您将为 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'); 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 控制台(例如,当 从 OpenAPI 导入 API 时),您必须显式创建(如有必要)并设置调用角色和策略以便 API Gateway 调用 Lambda 函数。有关如何为 API Gateway API 设置 Lambda 调用和执行角色的更多信息,请参阅 使用 IAM 许可控制对 API 的访问

与用于 Lambda 代理集成的 Lambda 函数的 GetStartedLambdaProxyIntegation 相比,用于 Lambda 自定义集成的 Lambda 函数的 GetStartedLambdaIntegration 仅从 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. 请执行下列操作之一:

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

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

  3. 选择 Author from scratch (从头开始创作)

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

    1. 对于名称,键入 GetStartedLambdaIntegration 作为 Lambda 函数名称。

    2. 对于 Runtime (运行时),选择 Node.js 8.10

    3. 对于角色,选择 Create new role from template(s)

    4. 对于角色名称,键入一个角色名称(例如 GetStartedLambdaIntegrationRole)。

    5. 对于策略模板,选择 Simple Microservice permissions

    6. 选择 Create function (创建函数)

  5. Function code (函数代码) 下的 Configure function (配置函数) 窗格中,执行以下操作:

    1. 选择 Edit code inline (编辑代码内联),如果尚未显示,则在 Content entry type (内容条目类型) 下:

    2. 处理程序设置保留为 index.handler

    3. Runtime (运行时) 设置为 Node.js 8.10

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

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

    6. 选择 Save (保存)

  6. 要测试新创建的函数,最好的做法是从 Select a test event...(选择一个测试事件) 中选择 Configure test events (配置测试事件)

    1. 对于 Create new event (创建新事件),使用以下内容替换所有默认代码语句,为事件名称键入 HellowWorldTest,然后选择创建

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

      { "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 函数的要求将客户端提供的请求数据转换为有效格式。

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

  1. 启动 API Gateway 控制台。

  2. 选择 Create new API (创建新 API)

    1. API name (API 名称) 键入 GetStartedLambdaNonProxyIntegration

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

    3. 选择 Create API (创建 API)

  3. 资源下选择根资源 (/)。从操作菜单中,选择创建资源

    1. 对于 Resource Name (资源名称),键入 city

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

    3. 选择 Enable API Gateway Cors (启用 &ABP Cors) 选项。

    4. 选择 Create Resource (创建资源)

  4. 突出显示新创建的 /{city} 资源,然后从操作中选择创建方法

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

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

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

    1. 对于 Integration type 选择 Lambda Function (Lambda 函数)

    2. 请勿选中 Use Lambda Proxy integration (使用 Lambda 代理集成) 复选框。

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

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

    5. 请勿选中 Use Default timeout (使用默认超时) 复选框。

    6. 选择 Save (保存)

    7. Add Permission to Lambda Function (为 Lambda 函数添加权限) 弹出窗口中选择确定,让 API Gateway 设置 API 调用集成的 Lambda 函数所需的访问权限。

  6. 在本步骤中,您将进行以下配置:

    • 查询字符串参数 (time)

    • 标头参数 (day)

    • 负载属性 (callerName)

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

    1. Method Execution (方法执行) 窗格中,选择 Method Request (方法请求)

    2. 展开 URL Query String Parameters (URL 查询字符串参数) 部分。选择 Add query string (添加查询字符串)。为名称键入 time。选择必须选项,并选择复选标记以保存设置。清除缓存以避免此练习产生不必要的费用。

    3. 展开 HTTP Request Headers (HTTP 请求标头) 部分。选择 Add header (添加标头)。为名称键入 day。选择必须选项,并选择复选标记以保存设置。清除缓存以避免此练习产生不必要的费用。

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

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

      2. 对于模型名称,键入 GetStartedLambdaIntegrationUserInput

      3. 内容类型键入 application/json

      4. 模型描述留空。

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

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

      7. 依次选择资源/{city} ANY 方法、Method Request (方法请求),并展开 Request body (请求正文)。选择 Add model (添加模型)。为内容类型键入application/json。为 Model name (模型名称) 选择 GetStartedLambdaIntegrationInput。选择对勾图标以保存设置。

  7. 依次选择 /{city} ANY 方法和 Integration Request (集成请求) 以设置正文映射模板。在此步骤中,您将根据后端 Lambda 函数的要求将先前配置的 nameQuerynameHeader 的方法请求参数映射为 JSON 负载。

    1. 展开 Mapping Templates (映射模板) 部分。选择 Add mapping template (添加映射模板)。为内容类型键入 application/json。选择对勾图标以保存设置。

    2. 在弹出窗口中,选择 Yes, secure this integration (是,锁定此集成)

    3. 对于 Request body passthrough (请求正文传递),选中建议的 When there are no templates defined

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

    5. 使用以下内容替换映射模板编辑器中生成的映射脚本,如下所示:

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

测试 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 (方法执行) 中,选择测试

  2. 方法下拉列表中选择 POST

  3. 路径中,键入 Seattle

  4. Query Strings (查询字符串) 中,键入 time=morning

  5. Headers (标头) 中,键入 day:Wednesday

  6. Request Body (请求正文) 中,键入 { "callerName":"John" }

  7. 选择 Test (测试)

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

    { "greeting": "Good morning, John of Seattle. Happy Wednesday!" }
  9. 您还可以查看日志,以确定 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 或从资源窗格中选择资源或方法。从操作下拉菜单中选择 Deploy API (部署 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)。此基本 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,则会获得相同的响应。

  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,再选择操作,然后选择 Delete function (删除函数)。当系统提示时,再次选择删除

  3. 从函数列表中选择 GetHelloWithName,再选择操作,然后选择 Delete function (删除函数)。当系统提示时,再次选择删除

删除相关联的 IAM 资源

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

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

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

  4. 详细信息中,选择策略

  5. 从策略列表中选择 APIGatewayLambdaExecPolicy,再选择 Policy Actions (策略操作),然后选择 删除。系统提示时,选择删除

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