

# 为 API Gateway 中的 WebSocket API 创建路由
<a name="websocket-api-develop-routes"></a>

在 WebSocket API 中，传入的 JSON 消息将根据您配置的路由定向到后端集成。（非 JSON 消息将定向到您配置的 `$default` 路由。）

*路由*包含一个*路由键*，这是在评估*路由选择表达式*时预期的值。`routeSelectionExpression` 是在 API 级别定义的属性。它指定了预期存在于消息负载中的 JSON 属性。有关路由选择表达式的更多信息，请参阅[路由选择表达式](#apigateway-websocket-api-route-selection-expressions)。

例如，如果您的 JSON 消息包含一个 `action` 属性，并且您想要根据此属性执行不同操作，则您的路由选择表达式可能是 `${request.body.action}`。您的路由表将通过将 `action` 属性的值与您在表中定义的自定义路由键值相匹配来指定要执行的操作。

可以使用三个预定义路由：`$connect`、`$disconnect` 和 `$default`。此外，您还可以创建自定义路由。
+ API Gateway 会在客户端和 WebSocket API 之间的持久连接处于启动状态时调用 `$connect` 路由。
+ API Gateway 会在客户端或服务器与 API 断开连接时调用 `$disconnect` 路由。
+ 如果找到匹配的路由，则 API Gateway 会在针对消息评估路由选择表达式之后调用自定义路由；匹配项确定调用哪个集成。
+ 如果无法针对消息评估路径选择表达式或未找到匹配的路由，则 API Gateway 会调用 `$default` 路由。

## 路由选择表达式
<a name="apigateway-websocket-api-route-selection-expressions"></a>

当服务正在为传入消息选择要遵循的路由时，将会对*路由选择表达式*进行求解。该服务使用其 `routeKey` 与求解值完全匹配的路由。如果没有匹配项，并且存在具有 `$default` 路由键的路由，则将选择该路由。如果没有路由与求解值匹配，并且没有 `$default` 路由，则该服务将返回错误。对于基于 WebSocket 的 API，表达式的格式应为 `$request.body.{path_to_body_element}`。

例如，假设您要发送以下 JSON 消息：

```
{
    "service" : "chat",
    "action" : "join",
    "data" : {
        "room" : "room1234"
   }
}
```

您可能希望根据 `action` 属性选择 API 的行为。在这种情况下，您可以定义以下路由选择表达式：

```
$request.body.action
```

在此示例中，`request.body` 是指您的消息的 JSON 负载，`.action` 是 [JSONPath](https://goessner.net/articles/JsonPath/) 表达式。您可以在 `request.body` 之后使用任何 JSON 路径表达式，但请记住，结果将会字符串化。例如，如果您的 JSONPath 表达式返回包含两个元素的数组，那么它将显示为字符串 `"[item1, item2]"`。因此，最好将表达式求解为值而不是数组或对象。

您可以仅使用一个静态值，也可以使用多个变量。下表显示了针对上述负载的示例及其求解结果。


| 表达式 | 求解结果 | 说明 | 
| --- | --- | --- | 
| \$1request.body.action | join | 未包装的变量 | 
| \$1\$1request.body.action\$1 | join | 包装的变量 | 
| \$1\$1request.body.service\$1/\$1\$1request.body.action\$1 | chat/join | 具有静态值的多个变量 | 
| \$1\$1request.body.action\$1-\$1\$1request.body.invalidPath\$1  | join- | 如果找不到 JSONPath，则变量将解析为 ""。 | 
| action | action | 静态值 | 
| \$1\$1default | \$1default | 静态值 | 

求解结果将用于查找路由。如果存在具有匹配路由键的路由，则将选择该路由来处理消息。如果找不到匹配的路由，则 API Gateway 尝试查找 `$default` 路由（如果可用）。如果未定义 `$default` 路由，则 API Gateway 将返回错误。

## 在 API Gateway 中为 WebSocket API 设置路由
<a name="apigateway-websocket-api-routes"></a>

首次创建新的 WebSocket API 时，有三个预定义的路由：`$connect`、`$disconnect` 和 `$default`。您可以使用控制台、API 或 AWS CLI 创建它们。如果需要，您可以创建自定义路由。有关更多信息，请参阅 [API Gateway 中的 WebSocket API 概览](apigateway-websocket-api-overview.md)。

**注意**  
在 CLI 中，您可以在创建集成之前或之后创建路由，并且可以为多个路由重用相同的集成。

### 使用 API Gateway 控制台创建路由
<a name="apigateway-websocket-api-route-using-console"></a>

**使用 API Gateway 控制台创建路由**

1. 登录到 API Gateway 控制台，选择 API，然后选择**路由**。

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

1. 在**路径密钥**中，输入路径密钥名称。您可以创建预定义路由（`$connect`、`$disconnect` 和 `$default`），也可以创建自定义路由。
**注意**  
当您创建自定义路由时，请勿在路由键名称中使用 `$` 前缀。此前缀是专为预定义路由预留的。

1. 选择并配置路径的集成类型。有关更多信息，请参阅 [使用 API Gateway 控制台设置 WebSocket API 集成请求](apigateway-websocket-api-integration-requests.md#apigateway-websocket-api-integration-request-using-console)。

### 使用 AWS CLI 创建路由
<a name="apigateway-websocket-api-route-using-awscli"></a>

以下 [create-route](https://docs.aws.amazon.com/cli/latest/reference/apigatewayv2/create-route.html) 命令创建路由：

```
aws apigatewayv2 --region us-east-1 create-route --api-id aabbccddee --route-key $default
```

输出与以下内容类似：

```
{
    "ApiKeyRequired": false,
    "AuthorizationType": "NONE",
    "RouteKey": "$default",
    "RouteId": "1122334"
}
```

### 为 `$connect` 指定路由请求设置
<a name="apigateway-websocket-api-route-request-connect"></a>

当您为 API 设置 `$connect` 路由时，可以使用以下可选设置为 API 启用授权。有关更多信息，请参阅 [`$connect` 路由](apigateway-websocket-api-route-keys-connect-disconnect.md#apigateway-websocket-api-routes-about-connect)。
+ **Authorization (授权)**：如果不需要授权，您可以指定 `NONE`。否则，您可以指定：
  + `AWS_IAM`，以使用标准AWS IAM 策略来控制对 API 的访问。
  + `CUSTOM`，以通过指定先前创建的 Lambda 授权方函数来实现 API 的授权。授权方可以驻留在您自己的AWS账户或其他AWS账户中。有关 Lambda 授权方的更多信息，请参阅 [使用 API Gateway Lambda 授权方](apigateway-use-lambda-authorizer.md)。
**注意**  
在 API Gateway 控制台中，只有在您设置了如`CUSTOM`中所述的授权方函数后，[配置 Lambda 授权方（控制台）](configure-api-gateway-lambda-authorization.md#configure-api-gateway-lambda-authorization-with-console) 设置才可见。
**重要**  
**Authorization (授权)** 设置将会应用于整个 API，而不仅仅是 `$connect` 路由。`$connect` 路由保护其他路由，因为它在每个连接上都会被调用。
+ **需要 API 密钥**：您可以选择要求为 API 的 `$connect` 路由提供 API 密钥。您可以将 API 键与使用计划一起使用来控制和跟踪对 API 的访问。有关更多信息，请参阅 [API Gateway 中针对 REST API 的使用计划和 API 密钥](api-gateway-api-usage-plans.md)。

### 使用 API Gateway 控制台设置 `$connect` 路由请求
<a name="apigateway-websocket-api-connect-route-request-using-console"></a>

要使用 API Gateway 控制台为 WebSocket API 设置 `$connect` 路由请求，请执行以下操作：

1. 登录到 API Gateway 控制台，选择 API，然后选择**路由**。

1. 在**路由**下选择 `$connect`，或按照[使用 API Gateway 控制台创建路由](#apigateway-websocket-api-route-using-console)创建 `$connect` 路由。

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

1. 对于**授权**，选择一种授权类型。

1. 要为 `$connect` 路由要求 API，请选择**需要 API 密钥**。

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

# 为 API Gateway 中的 WebSocket API 设置路由响应
<a name="apigateway-websocket-api-route-response"></a>

WebSocket 路由可以配置为双向或单向通信。除非您设置了路由响应，否则 API Gateway 不会将后端响应传递给路由响应。

**注意**  
您只能为 WebSocket API 定义 `$default` 路由响应。您可以使用集成响应来处理来自后端服务的响应。有关更多信息，请参阅 [集成响应概述](apigateway-websocket-api-integration-responses.md#apigateway-websocket-api-integration-response-overview)。

您可以使用 API Gateway 控制台、AWS CLI 或AWS开发工具包配置路由响应和响应选择表达式。

有关路由响应选择表达式的更多信息，请参阅[路由响应选择表达式](apigateway-websocket-api-selection-expressions.md#apigateway-websocket-api-route-response-selection-expressions)。

**Topics**
+ [使用 API Gateway 控制台设置路由响应](#apigateway-websocket-api-route-response-using-console)
+ [使用 AWS CLI 设置路由响应](#apigateway-websocket-api-route-response-using-awscli)

## 使用 API Gateway 控制台设置路由响应
<a name="apigateway-websocket-api-route-response-using-console"></a>

在创建 WebSocket API 并将代理 Lambda 函数附加到默认路由后，您可以使用 API Gateway 控制台设置路由响应：

1. 登录 API Gateway 控制台，对于 `$default` 路由选择集成了代理 Lambda 函数的 WebSocket API。

1. 在 **Routes**（路由）下，选择 `$default` 路由。

1. 选择**启用双向通信**。

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

1. 将 API 部署到阶段。

 使用以下 [wscat](https://www.npmjs.com/package/wscat) 命令连接到您的 API。有关 `wscat` 的更多信息，请参阅[使用 `wscat` 连接到 WebSocket API 并向其发送消息](apigateway-how-to-call-websocket-api-wscat.md)。

```
wscat -c wss://api-id.execute-api.us-east-2.amazonaws.com/test
```

 按 Enter 键以调用默认路由。应返回您的 Lambda 函数的主体。

## 使用 AWS CLI 设置路由响应
<a name="apigateway-websocket-api-route-response-using-awscli"></a>

以下 [create-route-response](https://docs.aws.amazon.com/cli/latest/reference/apigatewayv2/create-route-response.html) 命令为 `$default` 路由创建路由响应。您可以使用 [get-apis](https://docs.aws.amazon.com/cli/latest/reference/apigatewayv2/get-apis.html) 和 [get-routes](https://docs.aws.amazon.com/cli/latest/reference/apigatewayv2/get-routes.html) 命令来确定 API ID 和路由 ID。

```
aws apigatewayv2 create-route-response \
    --api-id aabbccddee \
    --route-id 1122334  \
    --route-response-key '$default'
```

输出将与以下内容类似：

```
{
    "RouteResponseId": "abcdef",
    "RouteResponseKey": "$default"
}
```

# 设置需要 WebSocket 子协议的 `$connect` 路由
<a name="websocket-connect-route-subprotocol"></a>

在连接到 WebSocket API 期间，客户端可以使用 `Sec-WebSocket-Protocol` 字段请求 [WebSocket 子协议](https://datatracker.ietf.org/doc/html/rfc6455#page-12)。您可以为 `$connect` 路由设置集成，以便仅当客户端请求您的 API 支持的子协议时允许连接。

以下示例 Lambda 函数将 `Sec-WebSocket-Protocol` 标头返回给客户端。仅当客户端指定 `myprotocol` 子协议时，此函数才会建立与 API 的连接。

如需创建此示例 API 和 Lambda 代理集成的 CloudFormation 模板，请参阅 [samples/ws-subprotocol.zip](samples/ws-subprotocol.zip)。

```
export const handler = async (event) => {
    if (event.headers != undefined) {
        const headers = toLowerCaseProperties(event.headers);
        
        if (headers['sec-websocket-protocol'] != undefined) {
            const subprotocolHeader = headers['sec-websocket-protocol'];
            const subprotocols = subprotocolHeader.split(',');
            
            if (subprotocols.indexOf('myprotocol') >= 0) {
                const response = {
                    statusCode: 200,
                    headers: {
                        "Sec-WebSocket-Protocol" : "myprotocol"
                    }
                };
                return response;
            }
        }
    }
    
    const response = {
        statusCode: 400
    };
        
    return response;
};

function toLowerCaseProperties(obj) {
    var wrapper = {};
    for (var key in obj) {
        wrapper[key.toLowerCase()] = obj[key];
    }
    return wrapper;
}
```

您可以使用 [https://www.npmjs.com/package/wscat](https://www.npmjs.com/package/wscat) 测试您的 API，以确定是否仅当客户端请求 API 支持的子协议时允许连接。以下命令在连接过程中使用 `-s` 标志指定子协议。

以下命令尝试使用不受支持的子协议进行连接。由于客户端指定了 `chat1` 子协议，因此，Lambda 集成将返回 400 错误，并且连接将失败。

```
wscat -c wss://api-id.execute-api.region.amazonaws.com/beta -s chat1
error: Unexpected server response: 400
```

以下命令在连接请求中包含支持的子协议。Lambda 集成允许连接。

```
wscat -c wss://api-id.execute-api.region.amazonaws.com/beta -s chat1,myprotocol
connected (press CTRL+C to quit)
```

要了解有关调用 WebSocket API 的更多信息，请参阅[调用 WebSocket API](apigateway-how-to-call-websocket-api.md)。