

# 教程：利用 Amazon 集成创建 WebSocket API
<a name="websocket-api-step-functions-tutorial"></a>

在本教程中，您使用 WebSocket API 创建一个无服务器广播应用程序。客户端可以接收消息，而无需轮询更新。

 本教程介绍如何向连接的客户端广播消息，并包括 Lambda 授权方、模拟集成以及与 Step Functions 的非代理集成的示例。

使用 Amazon CloudFormation 模板创建资源后，您将使用 API Gateway 控制台来创建与 Amazon 资源集成的 WebSocket API。您需要将 Lambda 授权方附加到您的 API，并创建 Amazon 服务与 Step Functions 的集成，才能启动状态机执行。Step Functions 状态机将调用一个 Lambda 函数，来向所有连接的客户端发送消息。

构建 API 后，您将测试到 API 的连接，并验证消息是否已发送和接收。完成本教程需要大约 45 分钟。

**Topics**
+ [先决条件](#websocket-api-step-functions-prerequisites)
+ [步骤 1：创建资源](#websocket-api-step-functions-create-dependencies)
+ [步骤 2：创建 WebSocket API](#websocket-api-step-functions-create-api)
+ [步骤 3：创建 Lambda 授权方](#websocket-api-step-functions-create-authorizer)
+ [步骤 4：创建模拟双向集成](#websocket-api-step-functions-create-mock-integration)
+ [步骤 5：使用 Step Functions 创建非代理集成](#websocket-api-step-functions-create-step-function-integration)
+ [步骤 6：测试您的 API](#websocket-api-step-functions-test-api)
+ [步骤 7：清除](#websocket-api-step-functions-cleanup)
+ [后续步骤](#websocket-api-step-functions-next-steps)

## 先决条件
<a name="websocket-api-step-functions-prerequisites"></a>

您需要以下先决条件：
+ 拥有控制台访问权限的 Amazon 账户和 Amazon Identity and Access Management 用户。有关更多信息，请参阅 [设置为使用 API Gateway](setting-up.md)。
+ 要连接到您的 API 的 `wscat` 有关更多信息，请参阅 [使用 `wscat` 连接到 WebSocket API 并向其发送消息](apigateway-how-to-call-websocket-api-wscat.md)。

我们建议您在开始本教程之前，完成 WebSocket 聊天应用程序教程。要完成 WebSocket 聊天应用程序教程，请参阅[教程：使用 WebSocket API、Lambda 和 DynamoDB 创建 WebSocket 聊天应用程序](websocket-api-chat-app.md)。

## 步骤 1：创建资源
<a name="websocket-api-step-functions-create-dependencies"></a>

下载并解压缩[适用于 Amazon CloudFormation 的应用程序创建模板](samples/ws-sfn-starter.zip)。您将使用此模板创建以下各项：
+ 处理 API 请求并授权访问您的 API 的 Lambda 函数。
+ 用于存储客户端 ID 和由 Lambda 授权方返回的主体用户标识的 DynamoDB 表。
+ 用于向连接的客户端发送消息的 Step Functions 状态机。

**创建 Amazon CloudFormation 堆栈**

1. 打开 Amazon CloudFormation 控制台，地址：[https://console.aws.amazon.com/cloudformation](https://console.amazonaws.cn/cloudformation/)。

1. 选择**创建堆栈**，然后选择**使用新资源(标准)**。

1. 对于**指定模板**，选择**上传模板文件**。

1. 选择您下载的模板。

1. 选择**下一步**。

1. 对于**堆栈名称**，输入 **websocket-step-functions-tutorial**，然后选择**下一步**。

1. 对于**配置堆栈选项**，请选择**下一步**。

1. 对于**功能**，请确认 Amazon CloudFormation 可以在您的账户中创建 IAM 资源。

1. 选择**下一步**，然后选择**提交**。

Amazon CloudFormation 预置在模板中指定的资源。完成资源预置可能需要几分钟时间。选择**输出**选项卡，来查看您创建的资源及其 ARN。当 Amazon CloudFormation 堆栈的状态为 **CREATE\_COMPLETE** 时，您就可以继续下一步了。

## 步骤 2：创建 WebSocket API
<a name="websocket-api-step-functions-create-api"></a>

您将创建一个 WebSocket API 来处理客户端连接，并将请求路由到您在步骤 1 中创建的资源。

**创建 WebSocket API**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.amazonaws.cn/apigateway)。

1. 选择**创建 API**。对于 **WebSocket API**，选择**构建**。

1. 对于 **API 名称**，请输入 **websocket-step-functions-tutorial**。

1. 为 **IP 地址类型**选择 **IPv4**。

1. 对于**路由选择表达式**，输入 **request.body.action**。

   路由选择表达式确定当客户端发送消息时 API Gateway 调用的路由。

1. 选择**下一步**。

1. 对于**预定义路由**，选择**添加 $connect**、**添加 $disconnect**、**添加 $default**。

   **$connect** 和 **$disconnect** 路由是 API Gateway 在客户端连接到 API 或断开与 API 的连接时自动调用的特殊路由。当没有其它路由与请求匹配时，API Gateway 调用 **$default** 路由。创建 API 后，您将创建一个连接到 Step Functions 的自定义路由。

1. 选择**下一步**。

1. 对于 **$connect 的集成**，请执行以下操作：

   1. 对于**集成类型**，选择 **Lambda**。

   1. 对于 **Lambda 函数**，选择您在步骤 1 中使用 Amazon CloudFormation 创建的相应 **$connect** Lambda 函数。Lambda 函数名称应该以 **websocket-step** 开头。

1. 对于 **$disconnect 的集成**，请执行以下操作：

   1. 对于**集成类型**，选择 **Lambda**。

   1. 对于 **Lambda 函数**，选择您在步骤 1 中使用 Amazon CloudFormation 创建的相应 **$disconnect** Lambda 函数。Lambda 函数名称应该以 **websocket-step** 开头。

1. 对于 **$default 的集成**，请选择**模拟**。

   在模拟集成中，API Gateway 在没有集成后端的情况下管理路由响应。

1. 选择**下一步**。

1. 查看 API Gateway 为您创建的阶段。默认情况下，API Gateway 会创建名为**生产**的阶段，然后自动将您的 API 部署到该阶段。选择**下一步**。

1. 选择**创建和部署**。

## 步骤 3：创建 Lambda 授权方
<a name="websocket-api-step-functions-create-authorizer"></a>

要控制对您的 WebSocket API 的访问权限，您需要创建 Lambda 授权方。Amazon CloudFormation 模板为您创建了 Lambda 授权方函数。您可以在 Lambda 控制台中看到 Lambda 函数。名称应以 **websocket-step-functions-tutorial-AuthorizerHandler** 开头。除非 `Authorization` 标头是 `Allow`，否则此 Lambda 函数会拒绝对 WebSocket API 的所有调用。Lambda 函数还会将 `$context.authorizer.principalId` 变量传递给您的 API，稍后会在 DynamoDB 表中使用该变量来标识 API 调用方。

在此步骤中，您将配置 **$connect** 路由来使用 Lambda 授权方。

**创建 Lambda 授权方**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.amazonaws.cn/apigateway)。

1. 在主导航窗格中，选择**授权方**。

1. 选择**创建授权方**。

1. 对于**授权方名称**，输入 **LambdaAuthorizer**。

1. 对于**授权方 ARN**，输入由 Amazon CloudFormation 模板创建的授权方的名称。名称应以 **websocket-step-functions-tutorial-AuthorizerHandler** 开头。
**注意**  
我们建议不要为生产 API 使用此示例授权方。

1. 对于**身份来源类型**，选择**标头**。对于**键**，输入 **Authorization**。

1. 选择**创建授权方**。

创建授权方后，将其附加到您的 API 的 **$connect** 路由。

**将授权方附加到 $connect 路由**

1. 在主导航窗格中，选择**路由**。

1. 选择 **$connect** 路由。

1. 在**路由请求设置**部分中，选择**编辑**。

1. 对于**授权**，请选择下拉菜单，然后选择您的请求授权方。

1. 选择**保存更改**。

## 步骤 4：创建模拟双向集成
<a name="websocket-api-step-functions-create-mock-integration"></a>

接下来，您将为 **$default** 路由创建双向模拟集成。模拟集成可让您在不使用后端的情况下向客户端发送响应。当您为 **$default** 路由创建集成时，您可以向客户端展示如何与 API 进行交互。

您可以配置 **$default** 路由来通知客户端使用 **sendmessage** 路由。

**创建模拟请求**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.amazonaws.cn/apigateway)。

1. 选择 **$default** 路由，然后选择**集成请求**选项卡。

1. 对于**请求模板**，请选择**编辑**。

1. 对于**模板选择表达式**，输入 **200**，然后选择**编辑**。

1. 在**集成请求**选项卡上，对于**请求模板**，选择**创建模板**。

1. 对于**模板密钥**，输入 **200**。

1. 对于**生成模板**，输入以下映射模板：

   ```
   {"statusCode": 200}
   ```

   选择**创建模板**。

   结果应该类似以下内容：

1. 在 **$default 路由**窗格中，选择**启用双向通信**。

1. 选择**集成响应**选项卡，然后选择**创建集成响应**。

1. 对于**响应密钥**，输入 **$default**。

1. 对于**模板选择表达式**，输入 **200**。

1. 选择**创建响应**。

1. 在**响应模板**下，选择**创建模板**。

1. 对于**模板密钥**，输入 **200**。

1. 对于**响应模板**，输入以下映射模板：

   ```
   {"Use the sendmessage route to send a message. Connection ID: $context.connectionId"}
   ```

1. 选择**创建模板**。

   结果应该类似以下内容：

## 步骤 5：使用 Step Functions 创建非代理集成
<a name="websocket-api-step-functions-create-step-function-integration"></a>

接下来，创建 **sendmessage** 路由。客户端可以调用 **sendmessage** 路由，来向所有连接的客户端广播消息。**sendmessage** 路由已将非代理 Amazon 服务与 Amazon Step Functions 集成。该集成会针对 Amazon CloudFormation 模板为您创建的 Step Functions 状态机调用 [StartExecution](https://docs.amazonaws.cn/step-functions/latest/apireference/API_StartExecution.html) 命令。

**创建非代理集成**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.amazonaws.cn/apigateway)。

1. 选择**创建路由**。

1. 对于**路由键**，请输入 **sendmessage**。

1. 对于**集成类型**，选择 **Amazon 服务**。

1. 对于 **Amazon 区域**，输入您部署了 Amazon CloudFormation 模板的区域。

1. 对于 **Amazon 服务**，选择 **Step Functions**。

1. 对于 **HTTP 方法**，选择 **POST**。

1. 对于**操作名称**，输入 **StartExecution**。

1. 对于**执行角色**，输入由 Amazon CloudFormation 模板创建的执行角色。名称应为 **WebsocketTutorialApiRole**。

1. 选择**创建路由**。

接下来，您创建一个映射模板，来将请求参数发送到 Step Functions 状态机。

**创建映射模板**

1. 选择 **sendmessage** 路由，然后选择**集成请求**选项卡。

1. 在**请求模板**部分中，选择**编辑**。

1. 对于**模板选择表达式**，输入 **\\$default**。

1. 选择**编辑**。

1. 在**请求模板**部分，选择**创建模板**。

1. 对于**模板密钥**，输入 **\\$default**。

1. 对于**生成模板**，输入以下映射模板：

   ```
   #set($domain = "$context.domainName")
   #set($stage = "$context.stage")
   #set($body = $input.json('$'))
   #set($getMessage = $util.parseJson($body))
   #set($mymessage = $getMessage.message)
   {
   "input": "{\"domain\": \"$domain\", \"stage\": \"$stage\", \"message\": \"$mymessage\"}",
   "stateMachineArn": "arn:aws:states:{{us-east-2}}:{{123456789012}}:stateMachine:WebSocket-Tutorial-StateMachine"
   }
   ```

   将 {{stateMachineArn}} 替换为由 Amazon CloudFormation 创建的状态机的 ARN。

   映射模板执行以下操作：
   + 使用上下文变量 `domainName` 创建变量 `$domain`。
   + 使用上下文变量 `stage` 创建变量 `$stage`。

     `$domain` 和 `$stage` 变量是构建回调 URL 所必需的。
   + 接收传入的 `sendmessage` JSON 消息，并提取 `message` 属性。
   + 为状态机创建输入。输入是 WebSocket API 的域和阶段以及来自 `sendmessage` 路由的消息。

1. 选择**创建模板**。

您可以在 **$connect** 或 **$disconnect** 路由上创建非代理集成，来直接在 DynamoDB 表中添加或移除连接 ID，而无需调用 Lambda 函数。

## 步骤 6：测试您的 API
<a name="websocket-api-step-functions-test-api"></a>

接下来，您将部署和测试您的 API 来确保它正常工作。您将使用 `wscat` 命令连接到 API，然后，您将使用斜杠命令发送 ping 帧来检查与 WebSocket API 的连接。

**部署 API**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.amazonaws.cn/apigateway)。

1. 在主导航窗格中，选择**路由**。

1. 选择**部署 API**。

1. 对于**阶段**，选择**生产**。

1. （可选）对于**部署描述**，输入描述。

1. 选择**部署**。

部署 API 后，您可以调用它。使用调用 URL 来调用您的 API。

**获取您的 API 的调用 URL**

1. 选择 API。

1. 选择**阶段**，然后选择**生产**。

1. 记下 API 的 **WebSocket URL**。该 URL 应类似于 `wss://{{abcdef123}}.execute-api.{{us-east-2}}.amazonaws.com/production`。

现在您已经有了调用 URL，您可以测试与 WebSocket API 的连接了。

**测试与您的 API 的连接**

1. 使用以下命令连接到您的 API。首先，通过调用 `/ping` 路径来测试连接。

   ```
   wscat -c wss://{{abcdef123}}.execute-api.{{us-east-2}}.amazonaws.com/production -H "Authorization: Allow" --slash -P
   ```

   ```
   Connected (press CTRL+C to quit)
   ```

1. 输入以下命令来 ping 控制帧。您可以从客户端使用控制帧来实现保持活动目的。

   ```
   /ping
   ```

   结果应该类似以下内容：

   ```
   < Received pong (data: "")
   ```

现在您已经测试了连接，您可以测试 API 是否正常工作。在此步骤中，您打开一个新的终端窗口，以便 WebSocket API 可以向所有连接的客户端发送消息。

**要测试您的 API**

1. 打开一个新终端并使用以下参数再次运行 `wscat` 命令。

   ```
   wscat -c wss://{{abcdef123}}.execute-api.{{us-east-2}}.amazonaws.com/production -H "Authorization: Allow"
   ```

   ```
   Connected (press CTRL+C to quit)
   ```

1. API Gateway 根据 API 的路由请求选择表达式确定要调用的路由。您的 API 的路由选择表达式是 `$request.body.action`。因此，当您发送以下消息时，API Gateway 会调用 `sendmessage` 路由：

   ```
   {"action": "sendmessage", "message": "hello, from Step Functions!"}
   ```

   与路由关联的 Step Functions 状态机使用消息和回调 URL 调用 Lambda 函数。Lambda 函数调用 API Gateway 管理 API，并将消息发送给所有连接的客户端。所有客户端都会收到以下消息：

   ```
   < hello, from Step Functions!
   ```

现在您已经测试了 WebSocket API，可以断开与 API 的连接。

**从 API 断开连接**
+ 按 `CTRL+C` 以从 API 断开连接。

  当客户端与 API 断开连接时，API Gateway 会调用 API 的 **$disconnect** 路由。适用于 API 的 **$disconnect** 路由的 Lambda 集成会从 DynamoDB 中删除连接 ID。

## 步骤 7：清除
<a name="websocket-api-step-functions-cleanup"></a>

为避免不必要的成本，请删除作为本教程的一部分而创建的资源。以下步骤将删除您的 Amazon CloudFormation 堆栈和 WebSocket API。

**删除 WebSocket API**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.amazonaws.cn/apigateway)。

1. 在 **API** 页面上，选择您的 **websocket-api**。

1. 选择**操作**，选择**删除**，然后确认您的选择。

**删除 Amazon CloudFormation 堆栈**

1. 打开 Amazon CloudFormation 控制台，地址：[https://console.aws.amazon.com/cloudformation](https://console.amazonaws.cn/cloudformation/)。

1. 选择您的 Amazon CloudFormation 堆栈。

1. 选择**删除**，然后确认您的选择。

## 后续步骤
<a name="websocket-api-step-functions-next-steps"></a>

您可以自动创建和清理本教程中涉及的所有 Amazon 资源。有关本教程中自动执行这些操作的 Amazon CloudFormation 模板的示例，请参阅 [ws-sfn.zip](samples/ws-sfn-complete.zip)。