

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 在中使用实时数据应用程序的订阅 AWS AppSync
<a name="aws-appsync-real-time-data"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅[事件开发者指南 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

AWS AppSync允许您利用订阅来实现实时应用程序更新、推送通知等。当客户端调用 GraphQL 订阅操作时，由自动建立和维护安全 WebSocket 连接。 AWS AppSync然后，应用程序可以将数据源中的数据实时分发给订阅者，同时 AWS AppSync 持续管理应用程序的连接和扩展需求。以下各节将向您展示订阅 AWS AppSync的工作原理。

## GraphQL 架构订阅指令
<a name="graphql-schema-subscription-directives"></a>

中的订阅 AWS AppSync 是作为对突变的响应而调用的。这意味着您可以通过在突变上指定 GraphQL 架构指令来 AWS AppSync 实时创建任何数据源。

 AWS Amplify 客户端库会自动处理订阅连接管理。这些库使用 pure WebSockets 作为客户端和服务之间的网络协议。

**注意**  
要控制订阅连接时的授权，您可以使用 AWS Identity and Access Management (IAM)、、Amazon Cognito 身份池或 Amazon Cognito 用户池进行字段级授权。 AWS Lambda要对订阅进行精细的访问控制，您可以将解析器附加到订阅字段，并使用调用者和 AWS AppSync 数据源的身份执行逻辑。有关更多信息，请参阅 [配置授权和身份验证以保护您的 GraphQL APIs](security-authz.md)。

订阅由变更触发，并将变更选择集发送给订阅者。

以下示例展示了如何使用 GraphQL 订阅。它没有指定数据源，因为数据源可能是 Lambda、Amazon DynamoDB 或亚马逊服务。 OpenSearch 

要开始使用订阅，您必须将订阅入口点添加到您的架构中，如下所示：

```
schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}
```

假设有一个博客站点，您希望订阅新博文和现有博客的变更。为此，请在架构中添加以下 `Subscription` 定义：

```
type Subscription {
    addedPost: Post
    updatedPost: Post
    deletedPost: Post
}
```

进一步假设有以下变更：

```
type Mutation {
    addPost(id: ID! author: String! title: String content: String url: String): Post!
    updatePost(id: ID! author: String! title: String content: String url: String ups: Int! downs: Int! expectedVersion: Int!): Post!
    deletePost(id: ID!): Post!
}
```

对于希望收到通知的每个订阅，您可以添加 `@aws_subscribe(mutations: ["mutation_field_1", "mutation_field_2"])` 指令，使这些字段成为实时字段，如下所示：

```
type Subscription {
    addedPost: Post
    @aws_subscribe(mutations: ["addPost"])
    updatedPost: Post
    @aws_subscribe(mutations: ["updatePost"])
    deletedPost: Post
    @aws_subscribe(mutations: ["deletePost"])
}
```

由于 `@aws_subscribe(mutations: ["",..,""])` 使用变更输入数组，因此，您可以指定多个启动订阅的变更。如果您从客户端订阅，您的 GraphQL 查询可能是下面的样子：

```
subscription NewPostSub {
    addedPost {
        __typename
        version
        title
        content
        author
        url
    }
}
```

客户端连接和工具需要使用该订阅查询。

对于纯 WebSockets 客户端，选择集筛选是针对每个客户机进行的，因为每个客户机都可以定义自己的选择集。在这种情况下，订阅选择集必须是变更选择集的子集。例如，链接到变更 `addPost(...){id author title url version}` 的订阅 `addedPost{author title}` 仅接收文章的作者和标题。它不会接收其他字段。但是，如果变更在其选择集中缺少作者，则订阅者将获得作者字段的 `null` 值（或者，如果在架构中将作者字段定义为必填/非 Null 的情况下，将得到错误）。

使用 pure 时，订阅选择集是必不可少的 WebSockets。如果订阅中未明确定义字段，则 AWS AppSync 不会返回该字段。

在以上示例中，订阅没有参数。假设您的架构如下所示：

```
type Subscription {
    updatedPost(id:ID! author:String): Post
    @aws_subscribe(mutations: ["updatePost"])
}
```

在这种情况下，您的客户端定义了订阅，如下所示：

```
subscription UpdatedPostSub {
    updatedPost(id:"XYZ", author:"ABC") {
        title
        content
    }
}
```

您的架构中 `subscription` 字段的返回类型必须与相应的变更字段的返回类型匹配。在上一示例中，`addPost` 和 `addedPost` 返回的类型都是 `Post`。

要在客户端上设置订阅，请参阅[使用 Amplify 客户端构建客户端应用程序](building-a-client-app.md)。

## 使用订阅参数
<a name="using-subscription-arguments"></a>

使用 GraphQL 订阅的一个重要部分是，了解何时以及如何使用参数。您可以进行细微的更改，以修改何时以及如何向客户端通知发生的变更。为此，请参阅快速入门章节中创建“Todo”的示例架构。对于该示例架构，定义了以下变更：

```
type Mutation {
    createTodo(input: CreateTodoInput!): Todo
    updateTodo(input: UpdateTodoInput!): Todo
    deleteTodo(input: DeleteTodoInput!): Todo
}
```

在默认示例中，客户端可以使用不带参数的 `onUpdateTodo` `subscription` 订阅任何 `Todo` 的更新：

```
subscription OnUpdateTodo {
  onUpdateTodo {
    description
    id
    name
    when
  }
}
```

您可以使用 `subscription` 的参数对其进行筛选。例如，要仅在更新具有特定 `ID` 的 `todo` 时触发 `subscription`，请指定 `ID` 值：

```
subscription OnUpdateTodo {
  onUpdateTodo(id: "a-todo-id") {
    description
    id
    name
    when
  }
}
```

您也可以传递多个参数。例如，以下 `subscription` 说明了如何在特定地点和时间获取任何 `Todo` 更新的通知：

```
subscription todosAtHome {
  onUpdateTodo(when: "tomorrow", where: "at home") {
    description
    id
    name
    when
    where
  }
}
```

请注意，所有参数都是可选的。如果未在 `subscription` 中指定任何参数，您将订阅应用程序中发生的所有 `Todo` 更新。不过，您可以更新 `subscription` 的字段定义以要求使用 `ID` 参数。这会强制提供特定 `todo` 的响应，而不是提供所有 `todo` 的响应：

```
onUpdateTodo(
  id: ID!,
  name: String,
  when: String,
  where: String,
  description: String
): Todo
```

### 参数 null 值具有含义
<a name="argument-null-value-has-meaning"></a>

在中进行订阅查询时 AWS AppSync，`null`参数值对结果的筛选方式与完全省略参数的方式不同。

让我们回到创建 Todo 的 Todo API 示例。请参阅快速入门章节中的示例架构。

让我们修改架构以在 `Todo` 类型上包含新的 `owner` 字段，该字段描述所有者是谁。`owner` 字段不是必填字段，只能在 `UpdateTodoInput` 上设置该字段。请参阅以下简化架构版本：

```
type Todo {
  id: ID!
  name: String!
  when: String!
  where: String!
  description: String!
  owner: String
}

input CreateTodoInput {
  name: String!
  when: String!
  where: String!
  description: String!
}

input UpdateTodoInput {
  id: ID!
  name: String
  when: String
  where: String
  description: String
  owner: String
}

type Subscription {
    onUpdateTodo(
        id: ID,
        name: String,
        when: String,
        where: String,
        description: String
    ): Todo @aws_subscribe(mutations: ["updateTodo"])
}
```

以下订阅返回所有 `Todo` 更新：

```
subscription MySubscription {
  onUpdateTodo {
    description
    id
    name
    when
    where
  }
}
```

如果您修改前面的订阅以添加字段参数 `owner: null`，您现在会问一个不同的问题。该订阅现在注册客户端，以获得所有未提供所有者的 `Todo` 更新的通知。

```
subscription MySubscription {
  onUpdateTodo(owner: null) {
    description
    id
    name
    when
    where
  }
}
```

**注意**  
**自 2022 年 1 月 1 日起，MQTT WebSockets 已不再可用作 GraphQL 订阅的协议。 AWS AppSync APIsPure WebSockets 是中唯一支持的协议 AWS AppSync。**  
基于 2019 年 11 月之后发布的 AWS AppSync SDK 或 Amplify 库的客户 WebSockets 默认会自动使用 pure。将客户端升级到最新版本允许他们使用 AWS AppSync纯 WebSockets 引擎。  
Pur WebSockets e 具有更大的有效载荷大小 (240 KB)、更多的客户端选项和改进的 CloudWatch 指标。有关使用纯 WebSocket 客户端的更多信息，请参阅[在中构建实时 WebSocket 客户端 AWS AppSync](real-time-websocket-client.md)。

# 在中创建由无服务器 pub/sub APIs WebSockets 提供支持的通用部件 AWS AppSync
<a name="aws-appsync-real-time-create-generic-api-serverless-websocket"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅[事件开发者指南 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

有些应用程序只要求客户收听特定频道或主题的简单 WebSocket APIs 功能。对于没有特定形状或强类型要求的通用 JSON 数据，可以将其推送到使用纯粹且简单的发布-订阅 (Pub/Sub) 模式侦听这些通道之一的客户端。

通过在 API 后端 pub/sub WebSocket APIs 和客户端自动生成 GraphQL 代码，可以在几分钟内轻松实现 GraphQL 知识。 AWS AppSync 

## 创建和配置 pub-sub APIs
<a name="aws-appsync-real-time-enhanced-filtering-using-pub-sub-apis"></a>

首先，执行以下操作：

1. 登录 AWS 管理控制台 并打开[AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**控制面板**中，选择**创建 API**。

1. 在下一个屏幕上，选择**创建一个实时 API**，然后选择**下一步**。

1. 输入您的 pub/sub API 的友好名称。

1. 您可以启用[私有 API](https://docs.aws.amazon.com/appsync/latest/devguide/using-private-apis.html) 功能，但我们建议暂时关闭该功能。选择**下一步**。

1. 您可以选择使用自动生成有效的 pub/sub API WebSockets。我们建议暂时也关闭该功能。选择**下一步**。

1. 选择**创建 API**，然后等待几分钟。将在您的账户中创建一个新的预配置 AWS AppSync 发布/订阅 API。 AWS 

API 使用内置 AWS AppSync的本地解析器（有关使用本地解析器的更多信息，请参阅*《AWS AppSync 开发者指南》*中的[教程：本地解析器](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-local-resolvers-js.html)）来管理多个临时 pub/sub 通道和 WebSocket 连接，它仅根据频道名称自动向订阅的客户端传送和筛选数据。API 调用通过 API 密钥进行授权。

在部署 API 后，您需要执行几个额外的步骤以生成客户端代码，并将其与客户端应用程序集成在一起。本指南使用一个简单的 React Web 应用程序，以提供如何快速集成客户端的示例。

1. 首先，在本地计算机上使用 [NPM](https://www.npmjs.com/get-npm) 创建一个样板 React 应用程序：

   ```
   $ npx create-react-app mypubsub-app 
   $ cd mypubsub-app
   ```
**注意**  
该示例使用 [Amplify 库](https://docs.amplify.aws/lib/)将客户端连接到后端 API。不过，无需在本地创建 Amplify CLI 项目。虽然 React 是该示例中选择的客户端，但 Amplify 库还支持 iOS、Android 和 Flutter 客户端，并在这些不同的运行时系统中提供相同的功能。[支持的Amplify客户端提供了简单的抽象，只需几行代码即可与Gr AWS AppSync aphQL API后端进行交互，包括与实时协议完全兼容的内置 WebSocket功能：AWS AppSync WebSocket ](https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html)  

   ```
   $ npm install @aws-amplify/api
   ```

1. 在 AWS AppSync 控制台中，选择 “下载” **JavaScript**，**下载**包含 API 配置详细信息和生成的 GraphQL 操作代码的单个文件。

1. 将下载的文件复制到 React 项目中的 `/src` 文件夹。

1. 接下来，将现有的样板 `src/App.js` 文件内容替换为控制台中提供的示例客户端代码。

1. 使用以下命令在本地启动该应用程序：

   ```
   $ npm start
   ```

1. 要测试实时数据的发送和接收，请打开两个浏览器窗口并访问*localhost:3000*。示例应用程序配置为将通用 JSON 数据发送到名*robots*为的硬编码频道。

1.  在其中的一个浏览器窗口中，在文本框中输入以下 JSON Blob，然后单击**提交**：

   ```
   {
     "robot":"r2d2",
     "planet": "tatooine"
   }
   ```

两个浏览器实例都订阅了该*robots*频道并实时接收发布的数据，这些数据显示在 Web 应用程序的底部：

![\[适用于 pub/sub API 的 React 应用程序示例\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/pub-sub-react.png)


所有必需的 GraphQL API 代码（包括架构、解析器和操作）都会自动生成，以启用通用 pub/sub 用例。在后端，数据通过 GraphQL 突变发布到 AWS AppSync的实时端点，如下所示：

```
mutation PublishData {
    publish(data: "{\"msg\": \"hello world!\"}", name: "channel") {
        data
        name
    }
}
```

订阅者通过相关的 GraphQL 订阅访问发送到特定临时通道的发布数据：

```
subscription SubscribeToData {
    subscribe(name:"channel") {
        name
        data
    }
}
```

## 在现有应用程序中实现 pub- APIs sub
<a name="aws-appsync-real-time-enhanced-filtering-existing-apps"></a>

如果您只需要在现有应用程序中实现实时功能，则可以轻松地将此通用 pub/sub API 配置集成到任何应用程序或 API 技术中。虽然使用单个 API 端点通过 GraphQL 在单个网络调用中安全地访问、操作和合并来自一个或多个数据源的数据具有优势，但无需从头开始转换或重建基于 REST 的现有应用程序即可利用的 AWS AppSync实时功能。例如，您可以将现有 CRUD 工作负载放在单独的 API 端点中，客户端仅出于实时 pub/sub 目的从现有应用程序向通用 pub/sub API 发送和接收消息或事件。

# 在中定义增强型订阅过滤器 AWS AppSync
<a name="aws-appsync-real-time-enhanced-filtering"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅[事件开发者指南 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

在中 AWS AppSync，您可以使用支持其他逻辑运算符的筛选器，直接在 GraphQL API 订阅解析器中定义和启用用于在后端进行数据筛选的业务逻辑。您可以配置这些筛选条件，这与在客户端的订阅查询上定义的订阅参数不同。有关使用订阅参数的更多信息，请参阅[使用订阅参数](aws-appsync-real-time-data.md#using-subscription-arguments)。有关运算符列表，请参阅[AWS AppSync 解析器映射模板实用程序参考](resolver-util-reference.md)。

就本文而言，我们将实时数据筛选分为以下几个类别：
+ **基本筛选** - 根据订阅查询中的客户端定义的参数进行筛选。
+ **增强筛选**-基于 AWS AppSync 服务后端集中定义的逻辑进行筛选。

以下几节介绍了如何配置增强订阅筛选条件，并说明了它们的实际用途。

## 在 GraphQL 架构中定义订阅
<a name="aws-appsync-real-time-enhanced-filtering-using-subscription-filters"></a>

要使用增强订阅筛选条件，您可以在 GraphQL 架构中定义订阅，然后使用筛选扩展定义增强筛选条件。要说明增强型订阅筛选的工作原理 AWS AppSync，请使用以下 GraphQL 架构（它定义了票证管理系统 API）作为示例：

```
type Ticket {
	id: ID
	createdAt: AWSDateTime
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
	status: String
	
}

type Mutation {
	createTicket(input: TicketInput): Ticket
}

type Query {
	getTicket(id: ID!): Ticket
}

type Subscription {
	onSpecialTicketCreated: Ticket @aws_subscribe(mutations: ["createTicket"])
	onGroupTicketCreated(group: String!): Ticket @aws_subscribe(mutations: ["createTicket"])
}



enum Priority {
	none
	lowest
	low
	medium
	high
	highest
}

input TicketInput {
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
```

假设您为 API 创建一个 `NONE` 数据来源，然后使用该数据来源将一个解析器附加到 `createTicket` 变更。您的处理程序可能如下所示：

```
import { util } from '@aws-appsync/utils';

export function request(ctx) {
	return {
		payload: {
			id: util.autoId(),
			createdAt: util.time.nowISO8601(),
			status: 'pending',
			...ctx.args.input,
		},
	};
}

export function response(ctx) {
	return ctx.result;
}
```

**注意**  
增强筛选条件是在给定订阅的 GraphQL 解析器处理程序中启用的。有关更多信息，请参阅[解析器参考](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-js-version.html)。

要实施增强筛选条件的行为，您必须使用 `extensions.setSubscriptionFilter()` 函数定义一个筛选条件表达式，该表达式针对通过 GraphQL 变更发布并且订阅客户端可能感兴趣的数据进行评估。有关筛选扩展的更多信息，请参阅[扩展](https://docs.aws.amazon.com//appsync/latest/devguide/extensions-js.html)。

以下几节介绍了如何使用筛选扩展实施增强筛选条件。

## 使用筛选扩展创建增强订阅筛选条件
<a name="aws-appsync-real-time-enhanced-filtering-defining-filters"></a>

增强筛选条件是在订阅解析器的响应处理程序中以 JSON 格式编写的。可以将筛选条件组合到一个名为 `filterGroup` 的列表中。筛选条件是使用至少一个规则定义的，每个规则包含字段、运算符和值。让我们为 `onSpecialTicketCreated` 定义一个新的解析器以设置增强筛选条件。您可以在一个筛选条件中配置多个使用 AND 逻辑进行评估的规则，而一个筛选条件组中的多个筛选条件使用 OR 逻辑进行评估：

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = {
		or: [
			{ severity: { ge: 7 }, priority: { in: ['high', 'medium'] } },
			{ category: { eq: 'security' }, group: { in: ['admin', 'operators'] } },
		],
	};
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

  // important: return null in the response
	return null;
}
```

根据前面示例中定义的筛选条件，如果使用以下条件创建了票证，则将重要票证自动推送到订阅的 API 客户端：
+ `priority` 级别 `high` 或 `medium`

  AND 
+ 大于或等于 `7` (`ge`) 的 `severity` 级别

或 
+ 设置为 `Security` 的 `classification` 票证 

  AND 
+ 设置为 `admin` 或 `operators` 的 `group` 分配

![\[显示票证筛选查询的示例\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/aws-priority-example.png)


订阅解析器中定义的筛选条件（增强筛选）优先于仅基于订阅参数的筛选（基本筛选）。有关使用订阅参数的更多信息，请参阅[使用订阅参数](https://docs.aws.amazon.com//appsync/latest/devguide/aws-appsync-real-time-data.html#using-subscription-arguments)。

如果在订阅的 GraphQL 架构中定义某个参数，并且该参数是必需的，只有在解析器的 `extensions.setSubscriptionFilter()` 方法中将给定参数定义为规则时，才会根据该参数进行筛选。不过，如果在订阅解析器中没有 `extensions` 筛选方法，则客户端中定义的参数仅用于基本筛选。您不能同时使用基本筛选和增强筛选。

您可以在订阅的筛选条件扩展逻辑中使用 [`context` 变量](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)，以访问有关请求的上下文信息。例如，在使用 Amazon Cognito 用户池、OIDC 或 Lambda 自定义授权者进行授权时，您可以在设置订阅后在 `context.identity` 中检索有关您的用户的信息。您可以使用该信息根据用户身份设置筛选条件。

现在假设您希望实施 `onGroupTicketCreated` 的增强筛选条件行为。`onGroupTicketCreated` 订阅要求将必需的 `group` 名称作为参数。在创建后，将自动为票证分配 `pending` 状态。您可以设置订阅筛选条件，以仅接收属于提供的组的新创建的票证：

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = { group: { eq: ctx.args.group }, status: { eq: 'pending' } };
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

	return null;
}
```

在使用变更发布数据时，如以下示例中所示：

```
mutation CreateTicket {
  createTicket(input: {priority: medium, severity: 2, group: "aws"}) {
    id
    priority
    severity
    status
    group
    createdAt
  }
}
```

订阅的客户端会在创建带有`createTicket`变异的票证后立即监听要自动推送 WebSockets的数据：

```
subscription OnGroup {
  onGroupTicketCreated(group: "aws") {
    category
    status
    severity
    priority
    id
    group
    createdAt
    content
  }
}
```

无需参数即可订阅客户端，因为筛选逻辑是在 AWS AppSync 服务中实现的，具有增强筛选功能，从而简化了客户端代码。只有在满足定义的筛选条件时，客户端才会收到数据。

## 为嵌套的架构字段定义增强筛选条件
<a name="aws-appsync-real-time-enhanced-filters-nested-schema-fields.title"></a>

您可以使用增强订阅筛选以筛选嵌套的架构字段。假设我们修改了上一节中的架构以包含位置和地址类型：

```
type Ticket {
	id: ID
	createdAt: AWSDateTime
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
	status: String
	location: ProblemLocation
}

type Mutation {
	createTicket(input: TicketInput): Ticket
}

type Query {
	getTicket(id: ID!): Ticket
}

type Subscription {
	onSpecialTicketCreated: Ticket @aws_subscribe(mutations: ["createTicket"])
	onGroupTicketCreated(group: String!): Ticket @aws_subscribe(mutations: ["createTicket"])
}

type ProblemLocation {
	address: Address
}

type Address {
	country: String
}

enum Priority {
	none
	lowest
	low
	medium
	high
	highest
}

input TicketInput {
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
	location: AWSJSON
```

对于该架构，您可以使用 `.` 分隔符表示嵌套。以下示例为 `location.address.country` 下面的嵌套架构字段添加筛选规则。如果票证的地址设置为 `USA`，则会触发订阅：

```
import { util, extensions } from '@aws-appsync/utils';

export const request = (ctx) => ({ payload: null });

export function response(ctx) {
	const filter = {
		or: [
			{ severity: { ge: 7 }, priority: { in: ['high', 'medium'] } },
			{ category: { eq: 'security' }, group: { in: ['admin', 'operators'] } },
			{ 'location.address.country': { eq: 'USA' } },
		],
	};
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));
	return null;
}
```

在上面的示例中，`location` 表示嵌套级别 1，`address` 表示嵌套级别 2，`country` 表示嵌套级别 3，所有这些字段以 `.` 分隔符分隔。

您可以使用 `createTicket` 变更测试该订阅：

```
mutation CreateTicketInUSA {
  createTicket(input: {location: "{\"address\":{\"country\":\"USA\"}}"}) {
    category
    content
    createdAt
    group
    id
    location {
      address {
        country
      }
    }
    priority
    severity
    status
  }
}
```

## 从客户端中定义增强筛选条件
<a name="aws-appsync-real-time-enhanced-filtering-defining-from-client"></a>

您可以在 GraphQL 中通过[订阅参数](https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-data.html#using-subscription-arguments)使用基本筛选。在订阅查询中进行调用的客户端定义参数的值。当在带有筛选功能的 AWS AppSync 订阅解析器中启用增强型`extensions`过滤器时，解析器中定义的后端过滤器具有优先级和优先级。

使用 `filter` 参数在订阅中配置客户端定义的动态增强筛选条件。在配置这些筛选条件时，您必须更新 GraphQL 架构以反映新参数：

```
...
type Subscription {
    onSpecialTicketCreated(filter: String): Ticket
        @aws_subscribe(mutations: ["createTicket"])
}
...
```

然后，客户端可以发送订阅查询，如以下示例中所示：

```
subscription onSpecialTicketCreated($filter: String) {
     onSpecialTicketCreated(filter: $filter) {
        id
        group
        description
        priority
        severity
     }
 }
```

您可以像以下示例一样配置查询变量：

```
{"filter" : "{\"severity\":{\"le\":2}}"}
```

可以在订阅响应映射模板中实施 `util.transform.toSubscriptionFilter()` 解析器实用程序，以应用每个客户端的订阅参数中定义的筛选条件：

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = ctx.args.filter;
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));
	return null;
}
```

通过使用该策略，客户端可以定义自己的筛选条件，以使用增强筛选逻辑和额外的运算符。当给定客户端在安全 WebSocket 连接中调用订阅查询时，就会分配过滤器。有关用于增强筛选的转换实用程序的更多信息，包括`filter`查询变量负载的格式，请参阅[JavaScript解析器概述](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html)。

## 额外的增强筛选限制
<a name="aws-appsync-real-time-enhanced-filtering-additional-restrictions"></a>

以下是几个对增强筛选条件施加额外限制的使用案例：
+ 增强筛选条件不支持顶级对象列表筛选。在该使用案例中，对于增强订阅，将忽略来自变更的发布数据。
+ AWS AppSync 支持多达五个嵌套级别。将忽略超过嵌套级别 5 的架构字段的筛选条件。请考虑下面的 GraphQL 响应。允许使用 `venue.address.country.metadata.continent` 中的 `continent` 字段，因为它是 5 级嵌套。不过，`venue.address.country.metadata.capital.financial` 中的 `financial` 是 6 级嵌套，因此，筛选条件不起作用：

  ```
  {
      "data": {
          "onCreateFilterEvent": {
              "venue": {
                  "address": {
                      "country": {
                          "metadata": {
                              "capital": {
                                  "financial": "New York"
                              },
                              "continent" : "North America"
                          }
                      },
                      "state": "WA"
                  },
                  "builtYear": 2023
              },
              "private": false,
          }
      }
  }
  ```

# 使用中的过滤器取消订阅 WebSocket 连接 AWS AppSync
<a name="aws-appsync-real-time-invalidation"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅[事件开发者指南 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

在中 AWS AppSync，您可以根据特定的筛选逻辑强制取消订阅并关闭（使 WebSocket连接失效）来自已连接客户端的连接。这在与授权相关的场景中非常有用，例如，在您从组中删除用户时。

订阅失效是针对变更中定义的负载做出的响应。我们建议您将用于使订阅连接失效的变更视为 API 中的管理操作，并仅限管理员用户、组或后端服务使用这些变更以相应地设置其权限范围。例如，使用架构授权指令，例如 `@aws_auth(cognito_groups: ["Administrators"])` 或 `@aws_iam`。有关更多信息，请参阅[使用其他授权模式](https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html#using-additional-authorization-modes)。

失效筛选条件使用与[增强订阅筛选条件](https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-enhanced-filtering.html)相同的语法和逻辑。请使用以下实用程序定义这些筛选条件：
+ `extensions.invalidateSubscriptions()` - 在变更的 GraphQL 解析器响应处理程序中定义。
+ `extensions.setSubscriptionInvalidationFilter()` - 在链接到变更的订阅的 GraphQL 解析器响应处理程序中定义。

有关失效筛选扩展插件的更多信息，请参阅[JavaScript解析器](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html)概述。

## 使用订阅失效
<a name="aws-appsync-real-time-invalidation-using-invalidations"></a>

要查看订阅失效的工作原理 AWS AppSync，请使用以下 GraphQL 架构：

```
type User {
  userId: ID!
  groupId: ID!
}
    
type Group {
  groupId: ID!
  name: String!
  members: [ID!]!
}

type GroupMessage {
  userId: ID!
  groupId: ID!
  message: String!
}

type Mutation {
    createGroupMessage(userId: ID!, groupId : ID!, message: String!): GroupMessage
    removeUserFromGroup(userId: ID!, groupId : ID!) : User @aws_iam
}

type Subscription {
    onGroupMessageCreated(userId: ID!, groupId : ID!): GroupMessage
        @aws_subscribe(mutations: ["createGroupMessage"])
}

type Query {
	none: String
}
```

在 `removeUserFromGroup` 变更解析器代码中定义失效筛选条件：

```
import { extensions } from '@aws-appsync/utils';

export function request(ctx) {
	return { payload: null };
}

export function response(ctx) {
	const { userId, groupId } = ctx.args;
	extensions.invalidateSubscriptions({
		subscriptionField: 'onGroupMessageCreated',
		payload: { userId, groupId },
	});
	return { userId, groupId };
}
```

在调用变更时，`payload` 对象中定义的数据用于取消订阅 `subscriptionField` 中定义的订阅。还会在 `onGroupMessageCreated` 订阅的响应映射模板中定义一个失效筛选条件。

如果`extensions.invalidateSubscriptions()`有效载荷包含的 ID 与过滤器中定义的 IDs 来自已订阅客户端的 ID 相匹配，则相应的订阅将被取消订阅。此外， WebSocket 连接已关闭。定义 `onGroupMessageCreated` 订阅的订阅解析器代码：

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = { groupId: { eq: ctx.args.groupId } };
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

	const invalidation = { groupId: { eq: ctx.args.groupId }, userId: { eq: ctx.args.userId } };
	extensions.setSubscriptionInvalidationFilter(util.transform.toSubscriptionFilter(invalidation));

	return null;
}
```

请注意，订阅响应处理程序可以同时定义订阅筛选条件和失效筛选条件。

例如，假设客户端 A 使用以下订阅请求为具有 ID `user-1` 的新用户订阅具有 ID `group-1` 的组：

```
onGroupMessageCreated(userId : "user-1", groupId: :"group-1"){...}
```

AWS AppSync 运行订阅解析器，该解析器生成前面的`onGroupMessageCreated`响应映射模板中定义的订阅和失效过滤器。对于客户端 A，订阅筛选条件仅允许将数据发送到 `group-1`，并为 `user-1` 和 `group-1` 定义了失效筛选条件。

现在假设客户端 B 使用以下订阅请求为具有 ID `user-2` 的用户订阅具有 ID `group-2` 的组：

```
onGroupMessageCreated(userId : "user-2", groupId: :"group-2"){...}
```

AWS AppSync 运行订阅解析器，它会生成订阅和失效过滤器。对于客户端 B，订阅筛选条件仅允许将数据发送到 `group-2`，并为 `user-2` 和 `group-2` 定义了失效筛选条件。

接下来，假设使用变更请求创建 ID 为 `message-1` 的新组消息，如以下示例中所示：

```
createGroupMessage(id: "message-1", groupId :
      "group-1", message: "test message"){...}
```

与定义的过滤器匹配的订阅客户端会自动通过 WebSockets以下方式接收以下数据负载：

```
{
  "data": {
    "onGroupMessageCreated": {
      "id": "message-1",
      "groupId": "group-1",
      "message": "test message",
    }
  }
}
```

客户端 A 收到该消息，因为筛选条件与定义的订阅筛选条件匹配。不过，客户端 B 不会收到该消息，因为该用户不属于 `group-1`。此外，请求与订阅解析器中定义的订阅筛选条件不匹配。

最后，假设使用以下变更请求从 `group-1` 中删除了 `user-1`：

```
removeUserFromGroup(userId: "user-1", groupId : "group-1"){...}
```

该突变会启动订阅失效，如其`extensions.invalidateSubscriptions()`解析器响应处理程序代码中所定义。 AWS AppSync 然后取消订阅客户端 A 并关闭其连接。 WebSocket 客户端 B 不受影响，因为变更中定义的失效负载与其用户或组不匹配。

当连接 AWS AppSync 失效时，客户端会收到一条消息，确认他们已取消订阅：

```
{
  "message": "Subscription complete."
}
```

## 在订阅失效筛选条件中使用上下文变量
<a name="aws-appsync-real-time-invalidation-context"></a>

与增强订阅筛选条件一样，您可以在订阅失效筛选条件扩展中使用 [`context` 变量](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)以访问某些数据。

例如，可以在变更中将电子邮件地址配置为失效负载，然后将其与通过 Amazon Cognito 用户池或 OpenID Connect 授权的订阅用户的电子邮件属性或声明进行匹配。失效筛选条件（在 `extensions.setSubscriptionInvalidationFilter()` 订阅失效器中定义）检查变更的 `extensions.invalidateSubscriptions()` 负载设置的电子邮件地址是否与从用户 JWT 令牌的 `context.identity.claims.email` 中检索的电子邮件地址匹配，从而启动失效。

# 在中构建实时 WebSocket 客户端 AWS AppSync
<a name="real-time-websocket-client"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅《[事件开发者指南》 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

AWS AppSync的实时 WebSocket 客户端通过多步流程支持 GraphQL 订阅。客户端首先与 AWS AppSync 实时端点建立 WebSocket 连接，发送连接初始化消息，然后等待确认。成功连接后，客户端通过发送带有唯一查询 IDs 和 GraphQL 查询的启动消息来注册订阅。 AWS AppSync 使用确认消息确认订阅成功。然后客户端侦听订阅事件，这些事件由相应的变更触发。为了保持连接， AWS AppSync 会定期发送保持活动状态的消息。完成后，客户端通过发送停止消息来取消注册订阅。该系统支持在单个 WebSocket 连接上进行多个订阅，并支持各种授权模式，包括 API 密钥、Amazon Cognito 用户池、IAM 和 Lambda。

## Graph WebSocket QL 订阅的实时客户端实现
<a name="appsynclong-real-time-websocket-client-implementation-guide-for-graphql-subscriptions"></a>

以下序列图和步骤显示了 WebSocket 客户端、HTTP 客户端和之间的实时订阅工作流程 AWS AppSync。

![\[Sequence diagram showing WebSocket client, AppSync endpoints, and HTTP client interactions for real-time subscriptions.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/realtime-client-flow.png)


1. 客户端与 AWS AppSync 实时端点建立 WebSocket 连接。如果存在网络错误，则客户端应该执行抖动指数退避。有关更多信息，请参阅架构[博客上的指数退避和抖动](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)。 AWS 

1. （可选）成功建立 WebSocket 连接后，客户端发送一条`connection_init`消息。

1. 如果`connection_init`已发送，则客户端将等待来自 AWS AppSync的`connection_ack`消息。该消息包含一个 `connectionTimeoutMs` 参数，它是 `"ka"`（保持活动）消息的最长等待时间（以毫秒为单位）。

1. AWS AppSync 定期发送`"ka"`消息。客户端跟踪它收到每条 `"ka"` 消息的时间。如果客户端在 `connectionTimeoutMs` 毫秒内未收到 `"ka"` 消息，客户端应关闭连接。

1. 客户端通过发送 `start` 订阅消息来注册订阅。单个 WebSocket 连接支持多个订阅，即使它们处于不同的授权模式。

1. 客户端等待发送`start_ack`消息 AWS AppSync 以确认订阅成功。如果出现错误，则 AWS AppSync 返回一条`"type": "error"`消息。

1. 客户端侦听订阅事件，这些事件是在调用相应变更后发送的。查询和突变通常会发送`https://`到 AWS AppSync GraphQL 端点。订阅通过 secure WebSocket (`wss://`) 流经 AWS AppSync 实时端点。

1. 客户端通过发送 `stop` 订阅消息来取消注册订阅。

1. 取消注册所有订阅并检查没有消息通过传输后 WebSocket，客户端可以断开与 WebSocket 连接的连接。

## 握手详情以建立连接 WebSocket
<a name="handshake-details-to-establish-the-websocket-connection"></a>

要与连接并成功启动握手 AWS AppSync， WebSocket 客户端需要满足以下条件：
+  AWS AppSync 实时终端节点
+ 标头-包含与 AWS AppSync 端点和授权相关的信息。 AWS AppSync 支持以下三种提供标头的方法：
  + 通过查询字符串提供标头
    + 标头信息编码为 base64 字符串，该字符串源自字符串化 JSON 对象。此 JSON 对象包含与 AWS AppSync 端点和授权相关的详细信息。JSON 对象的内容因授权模式而异。
  + 通过 `Sec-WebSocket-Protocol` 提供标头
    + 来自字符串化的 JSON 对象的 Base64URL 编码字符串作为协议在标头中传递，该字符串包含与 AWS AppSync端点和授权相关的信息。`Sec-WebSocket-Protocol`JSON 对象的内容因授权模式而异。
  + 通过标准 HTTP 标头提供标头：
    + 标头可以在连接请求中作为标准 HTTP 标头传递，类似于 GraphQL 查询和变更时传递标头的方式。 AWS AppSync但是，私有 API 连接请求不支持通过标准 HTTP 标头传递标头。
+  `payload` - `payload` 的 Base64 编码字符串。仅当使用查询字符串提供标头时，才需要有效载荷

根据这些要求， WebSocket 客户端可以使用`graphql-ws`作为 WebSocket 协议连接到 URL，该网址包含带有查询字符串的实时端点。

### 从 GraphQL 终端节点发现 实时终端节点
<a name="discovering-the-appsync-real-time-endpoint-from-the-appsync-graphql-endpoint"></a>

 AWS AppSync GraphQL 端点和 AWS AppSync 实时端点在协议和域上略有不同。您可以使用 AWS Command Line Interface (AWS CLI) 命令检索 GraphQL 端点。`aws appsync get-graphql-api`

****AWS AppSync GraphQL 端点：****  
 `https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql`

****AWS AppSync 实时端点：****  
 `wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql`

应用程序可以使用任何 HTTP 客户端连接到 AWS AppSync GraphQL 端点 (`https://`) 进行查询和变更。应用程序可以使用任何 WebSocket 客户端连接到 AWS AppSync 实时终端节点 (`wss://`) 进行订阅。

通过使用自定义域名，您可以使用单个域与两个终端节点进行交互。例如，如果您配置`api.example.com`为自定义域，则可以使用以下方式与GraphQL和实时终端节点进行交互： URLs

**AWS AppSync 自定义域 GraphQL 端点：**  
`https://api.example.com/graphql`

**AWS AppSync 自定义域名实时终端节点：**  
`wss://api.example.com/graphql/realtime`

## 基于 AWS AppSync API 授权模式的标头参数格式
<a name="header-parameter-format-based-on-appsync-api-authorization-mode"></a>

连接查询字符串中使用的`header`对象的格式因 AWS AppSync API 授权模式而异。对象中的`host`字段指的是 AWS AppSync GraphQL 端点，即使`wss://`调用是针对实时端点进行的，该端点也用于验证连接。要启动握手并建立授权连接，`payload` 应为空 JSON 对象。仅当通过查询字符串传递标头时，才需要有效载荷。

以下各节说明了每种授权模式的标头格式。

### API 密钥
<a name="api-key"></a>

#### API 密钥标头
<a name="api-key-list"></a>

**标头内容**
+  `"host": <string>`： AWS AppSyncGraphQL 终端节点的主机或您的自定义域名。
+  `"x-api-key": <string>`：为 API 配置的 AWS AppSync API 密钥。

**示例**

```
{
    "host":"example1234567890000.appsync-api.us-east-1.amazonaws.com",
    "x-api-key":"da2-12345678901234567890123456"
}
```

**通过查询字符串提供标头**

首先，将包含 `host` 和 `x-api-key` 的 JSON 对象转换为字符串。接下来，使用 base64 编码对该字符串进行编码。生成的 base64 编码字符串作为名为的查询参数添加`header`到 WebSocket URL 中，用于与实时端点建立连接。 AWS AppSync 生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJob3N0IjoiZXhhbXBsZTEyMzQ1Njc4OTAwMDAuYXBwc3luYy1hcGkudXMtZWFzdC0xLmFtYXpvbmF3cy5jb20iLCJ4LWFtei1kYXRlIjoiMjAyMDA0MDFUMDAxMDEwWiIsIngtYXBpLWtleSI6ImRhMi16NHc0NHZoczV6Z2MzZHRqNXNranJsbGxqaSJ9&payload=e30=
```

值得注意的是，除了 base64 编码的标头对象外，一个空的 JSON 对象 \$1\$1 也经过了 base64 编码，并作为单独的查询参数包含在网址中命名。`payload` WebSocket

**通过 `Sec-WebSocket-Protocol` 提供标头**

包含 `host` 和 `x-api-key` 的 JSON 对象会转换为字符串，然后使用 base64Url 编码进行编码。生成的 base64Url 编码字符串的前缀为 `header-`。然后，在与 AWS AppSync 实时端点建立 WebSocket 连接时，除了`Sec-WebSocket-Protocol`标头外，还会将`graphql-ws`此前缀字符串用作新的子协议。

生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 标头包含以下值：

```
"sec-websocket-protocol" : ["graphql-ws", "header-ewogICAgImhvc3QiOiJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsCiAgICAieC1hcGkta2V5IjoiZGEyLTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Igp9"]
```

**通过标准 HTTP 标头提供标头**

在这种方法中，在与 AWS AppSync 实时端点建立 WebSocket 连接时，使用标准 HTTP 标头传输主机和 API 密钥信息。生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

请求标头将包括以下内容：

```
"sec-websocket-protocol" : ["graphql-ws"]
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com",
"x-api-key":"da2-12345678901234567890123456"
```

### Amazon Cognito 用户池和 OpenID Connect (OIDC)
<a name="amazon-cognito-user-pools-and-openid-connect-oidc"></a>

#### 亚马逊 Cognito 和 OIDC 标题
<a name="amazon-cognito-user-pools-and-openid-connect-oidc-list"></a>

标头内容：
+  `"Authorization": <string>`：JWT ID 令牌。标头可以使用[持有者方案](https://datatracker.ietf.org/doc/html/rfc6750#section-2.1)。
+  `"host": <string>`： AWS AppSyncGraphQL 终端节点的主机或您的自定义域名。

示例：

```
{
    "Authorization":"eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJHWEFLSXBieU5WNHhsQjEXAMPLEnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyEXAMPLEiJhNmNmMjcwNy0xNjgxLTQ1NDItOWYxOC1lNjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImVkMzM5MmNkLWNjYTMtNGM2OC1hNDYyLTJlZGI3ZTNmY2FjZiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk0NTc3MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl83OHY0SVZibVAiLCJleHAiOjE1Njk0NjEzMjAsImlhdCI6MTU2OTQ1NzcyMCwianRpIjoiNTgzZjhmYmMtMzk2MS00YzA4LWJhZTAtYzQyY2IxMTM5NDY5IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1N3RoZWw0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3EXAMPLEn0.B4EXAMPLEFNpJ6ikVp7e6DRee95V6Qi-zEE2DJH7sHOl2zxYi7f-SmEGoh2AD8emxQRYajByz-rE4Jh0QOymN2Ys-ZIkMpVBTPgu-TMWDyOHhDUmUj2OP82yeZ3wlZAtr_gM4LzjXUXmI_K2yGjuXfXTaa1mvQEBG0mQfVd7SfwXB-jcv4RYVi6j25qgow9Ew52ufurPqaK-3WAKG32KpV8J4-Wejq8t0c-yA7sb8EnB551b7TU93uKRiVVK3E55Nk5ADPoam_WYE45i3s5qVAP_-InW75NUoOCGTsS8YWMfb6ecHYJ-1j-bzA27zaT9VjctXn9byNFZmEXAMPLExw",
    "host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
}
```

**通过查询字符串提供标头**

首先，将包含 `host` 和 `Authorization` 的 JSON 对象转换为字符串。接下来，使用 base64 编码对该字符串进行编码。生成的 base64 编码字符串作为名为的查询参数添加`header`到 WebSocket URL 中，用于与实时端点建立连接。 AWS AppSync生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoiZXlKcmFXUWlPaUpqYkc1eGIzQTVlVzVNSzA5UVlYSXJNVEpIV0VGTFNYQmllVTVXTkhoc1FqaFBWVzlZTW5NMldsZHZQU0lzSW1Gc1p5STZJbEpUTWpVMkluMC5leUp6ZFdJaU9pSmhObU5tTWpjd055MHhOamd4TFRRMU5ESXRPV1l4T0MxbE5qWTBNVGcyTmpsa016WWlMQ0psZG1WdWRGOXBaQ0k2SW1Wa016TTVNbU5rTFdOallUTXROR00yT0MxaE5EWXlMVEpsWkdJM1pUTm1ZMkZqWmlJc0luUnZhMlZ1WDNWelpTSTZJbUZqWTJWemN5SXNJbk5qYjNCbElqb2lZWGR6TG1OdloyNXBkRzh1YzJsbmJtbHVMblZ6WlhJdVlXUnRhVzRpTENKaGRYUm9YM1JwYldVaU9qRTFOamswTlRjM01UZ3NJbWx6Y3lJNkltaDBkSEJ6T2x3dlhDOWpiMmR1YVhSdkxXbGtjQzVoY0MxemIzVjBhR1ZoYzNRdE1pNWhiV0Y2YjI1aGQzTXVZMjl0WEM5aGNDMXpiM1YwYUdWaGMzUXRNbDgzT0hZMFNWWmliVkFpTENKbGVIQWlPakUxTmprME5qRXpNakFzSW1saGRDSTZNVFUyT1RRMU56Y3lNQ3dpYW5ScElqb2lOVGd6WmpobVltTXRNemsyTVMwMFl6QTRMV0poWlRBdFl6UXlZMkl4TVRNNU5EWTVJaXdpWTJ4cFpXNTBYMmxrSWpvaU0zRmxhalZsTVhabU16ZDFOM1JvWld3MGRHOTFkREprTVd3aUxDSjFjMlZ5Ym1GdFpTSTZJbVZzYjNKNllXWmxJbjAuQjRjZEp0aDNLRk5wSjZpa1ZwN2U2RFJlZTk1VjZRaS16RUUyREpIN3NIT2wyenhZaTdmLVNtRUdvaDJBRDhlbXhRUllhakJ5ei1yRTRKaDBRT3ltTjJZcy1aSWtNcFZCVFBndS1UTVdEeU9IaERVbVVqMk9QODJ5ZVozd2xaQXRyX2dNNEx6alhVWG1JX0syeUdqdVhmWFRhYTFtdlFFQkcwbVFmVmQ3U2Z3WEItamN2NFJZVmk2ajI1cWdvdzlFdzUydWZ1clBxYUstM1dBS0czMktwVjhKNC1XZWpxOHQwYy15QTdzYjhFbkI1NTFiN1RVOTN1S1JpVlZLM0U1NU5rNUFEUG9hbV9XWUU0NWkzczVxVkFQXy1Jblc3NU5Vb09DR1RzUzhZV01mYjZlY0hZSi0xai1iekEyN3phVDlWamN0WG45YnlORlptS0xwQTJMY3h3IiwiaG9zdCI6ImV4YW1wbGUxMjM0NTY3ODkwMDAwLmFwcHN5bmMtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tIn0=&payload=e30=
```

值得注意的是，除了 base64 编码的标头对象外，一个空的 JSON 对象 \$1\$1 也经过了 base64 编码，并作为单独的查询参数包含在网址中命名。`payload` WebSocket

**通过 `Sec-WebSocket-Protocol` 提供标头**

包含 `host` 和 `Authorization` 的 JSON 对象会转换为字符串，然后使用 base64Url 编码进行编码。生成的 base64Url 编码字符串的前缀为 `header-`。然后，在与 AWS AppSync 实时端点建立 WebSocket 连接时，除了`Sec-WebSocket-Protocol`标头外，还会将`graphql-ws`此前缀字符串用作新的子协议。

生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 标头包含以下值：

```
"sec-websocket-protocol" : ["graphql-ws", "header-ewogICAgImhvc3QiOiJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsCiAgICAieC1hcGkta2V5IjoiZGEyLTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Igp9"]
```

**通过标准 HTTP 标头提供标头**

在这种方法中，在与 AWS AppSync 实时端点建立 WebSocket 连接时，使用标准 HTTP 标头传输主机和授权信息。生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

请求标头将包括以下内容：

```
"sec-websocket-protocol" : ["graphql-ws"]
"Authorization":"eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJHWEFLSXBieU5WNHhsQjEXAMPLEnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyEXAMPLEiJhNmNmMjcwNy0xNjgxLTQ1NDItOWYxOC1lNjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImVkMzM5MmNkLWNjYTMtNGM2OC1hNDYyLTJlZGI3ZTNmY2FjZiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk0NTc3MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl83OHY0SVZibVAiLCJleHAiOjE1Njk0NjEzMjAsImlhdCI6MTU2OTQ1NzcyMCwianRpIjoiNTgzZjhmYmMtMzk2MS00YzA4LWJhZTAtYzQyY2IxMTM5NDY5IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1N3RoZWw0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3EXAMPLEn0.B4EXAMPLEFNpJ6ikVp7e6DRee95V6Qi-zEE2DJH7sHOl2zxYi7f-SmEGoh2AD8emxQRYajByz-rE4Jh0QOymN2Ys-ZIkMpVBTPgu-TMWDyOHhDUmUj2OP82yeZ3wlZAtr_gM4LzjXUXmI_K2yGjuXfXTaa1mvQEBG0mQfVd7SfwXB-jcv4RYVi6j25qgow9Ew52ufurPqaK-3WAKG32KpV8J4-Wejq8t0c-yA7sb8EnB551b7TU93uKRiVVK3E55Nk5ADPoam_WYE45i3s5qVAP_-InW75NUoOCGTsS8YWMfb6ecHYJ-1j-bzA27zaT9VjctXn9byNFZmEXAMPLExw",
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
```

### IAM
<a name="iam"></a>

#### IAM 标头
<a name="iam-list"></a>

**标头内容**
+  `"accept": "application/json, text/javascript"`：常量 `<string>` 参数。
+  `"content-encoding": "amz-1.0"`：常量 `<string>` 参数。
+  `"content-type": "application/json; charset=UTF-8"`：常量 `<string>` 参数。
+  `"host": <string>`：这是 AWS AppSync GraphQL 端点的主机。
  + `"x-amz-date": <string>`：时间戳必须以 UTC 表示并采用以下 ISO 8601 格式：YYYYMMDD'T'HHMMSS'Z'。例如，20150830T123600Z 是一个有效的时间戳。请勿在时间戳中包含毫秒。有关更多信息，请参阅《*AWS 一般参考*》中的[处理签名版本 4 的日期](https://docs.aws.amazon.com/general/latest/gr/sigv4-date-handling.html)。
  +  `"X-Amz-Security-Token": <string>`：会 AWS 话令牌，使用临时安全证书时是必需的。有关更多信息，请参阅《IAM 用户指南》**中的[将临时凭证用于 AWS 资源](https://docs.aws.amazon.com//IAM/latest/UserGuide/id_credentials_temp_use-resources.html)。
  +  `"Authorization": <string>`: 终端节点的签名版本 4 (Sigv4) 签名信息。 AWS AppSync 有关签名过程的更多信息，请参阅《*AWS 一般参考*》中的[任务 4：将签名添加到 HTTP 请求](https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html)。

Sigv4 签名 HTTP 请求包含一个规范 URL，它是附加了 `/connect` 的 AWS AppSync GraphQL 终端节点。服务终端节点 AWS 区域与您使用 AWS AppSync API 的区域相同，服务名称为 “appsync”。要签名的 HTTP 请求如下所示：

```
{
  url: "https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql/connect",
  data: "{}",
  method: "POST",
  headers: {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
  }
}
```

**示例**

```
{
  "accept": "application/json, text/javascript",
  "content-encoding": "amz-1.0",
  "content-type": "application/json; charset=UTF-8",
  "host": "example1234567890000.appsync-api.us-east-1.amazonaws.com",
  "x-amz-date": "20200401T001010Z",
  "X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
  "Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=83EXAMPLEbcc1fe3ee69f75cd5ebbf4cb4f150e4f99cec869f149c5EXAMPLEdc"
}
```

**通过查询字符串提供标头**

首先，将包含`host`（AWS AppSync GraphQL 端点）和其他授权标头的 JSON 对象转换为字符串。接下来，使用 base64 编码对该字符串进行编码。生成的 base64 编码字符串将作为名为的查询参数添加到 WebSocket URL 中。`header`生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoiZXlKcmFXUWlPaUpqYkc1eGIzQTVlVzVNSzA5UVlYSXJNVEpIV0VGTFNYQmllVTVXTkhoc1FqaFBWVzlZTW5NMldsZHZQU0lzSW1Gc1p5STZJbEpUTWpVMkluMC5leUp6ZFdJaU9pSmhObU5tTWpjd055MHhOamd4TFRRMU5ESXRPV1l4T0MxbE5qWTBNVGcyTmpsa016WWlMQ0psZG1WdWRGOXBaQ0k2SW1Wa016TTVNbU5rTFdOallUTXROR00yT0MxaE5EWXlMVEpsWkdJM1pUTm1ZMkZqWmlJc0luUnZhMlZ1WDNWelpTSTZJbUZqWTJWemN5SXNJbk5qYjNCbElqb2lZWGR6TG1OdloyNXBkRzh1YzJsbmJtbHVMblZ6WlhJdVlXUnRhVzRpTENKaGRYUm9YM1JwYldVaU9qRTFOamswTlRjM01UZ3NJbWx6Y3lJNkltaDBkSEJ6T2x3dlhDOWpiMmR1YVhSdkxXbGtjQzVoY0MxemIzVjBhR1ZoYzNRdE1pNWhiV0Y2YjI1aGQzTXVZMjl0WEM5aGNDMXpiM1YwYUdWaGMzUXRNbDgzT0hZMFNWWmliVkFpTENKbGVIQWlPakUxTmprME5qRXpNakFzSW1saGRDSTZNVFUyT1RRMU56Y3lNQ3dpYW5ScElqb2lOVGd6WmpobVltTXRNemsyTVMwMFl6QTRMV0poWlRBdFl6UXlZMkl4TVRNNU5EWTVJaXdpWTJ4cFpXNTBYMmxrSWpvaU0zRmxhalZsTVhabU16ZDFOM1JvWld3MGRHOTFkREprTVd3aUxDSjFjMlZ5Ym1GdFpTSTZJbVZzYjNKNllXWmxJbjAuQjRjZEp0aDNLRk5wSjZpa1ZwN2U2RFJlZTk1VjZRaS16RUUyREpIN3NIT2wyenhZaTdmLVNtRUdvaDJBRDhlbXhRUllhakJ5ei1yRTRKaDBRT3ltTjJZcy1aSWtNcFZCVFBndS1UTVdEeU9IaERVbVVqMk9QODJ5ZVozd2xaQXRyX2dNNEx6alhVWG1JX0syeUdqdVhmWFRhYTFtdlFFQkcwbVFmVmQ3U2Z3WEItamN2NFJZVmk2ajI1cWdvdzlFdzUydWZ1clBxYUstM1dBS0czMktwVjhKNC1XZWpxOHQwYy15QTdzYjhFbkI1NTFiN1RVOTN1S1JpVlZLM0U1NU5rNUFEUG9hbV9XWUU0NWkzczVxVkFQXy1Jblc3NU5Vb09DR1RzUzhZV01mYjZlY0hZSi0xai1iekEyN3phVDlWamN0WG45YnlORlptS0xwQTJMY3h3IiwiaG9zdCI6ImV4YW1wbGUxMjM0NTY3ODkwMDAwLmFwcHN5bmMtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tIn0=&payload=e30=
```

值得注意的是，除了 base64 编码的标头对象外，一个空的 JSON 对象 \$1\$1 也经过了 base64 编码，并作为单独的查询参数包含在网址中命名。`payload` WebSocket

**通过 `Sec-WebSocket-Protocol` 提供标头**

包含 `host` 和其他授权标头的 JSON 对象会转换为字符串，然后使用 base64Url 编码进行编码。生成的 base64Url 编码字符串的前缀为 `header-`。然后，在与 AWS AppSync 实时端点建立 WebSocket 连接时，除了`Sec-WebSocket-Protocol`标头外，还会将`graphql-ws`此前缀字符串用作新的子协议。

生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 标头包含以下值：

```
"sec-websocket-protocol" : ["graphql-ws", "header-ew0KICAiYWNjZXB0IjogImFwcGxpY2F0aW9uL2pzb24sIHRleHQvamF2YXNjcmlwdCIsDQogICJjb250ZW50LWVuY29kaW5nIjogImFtei0xLjAiLA0KICAiY29udGVudC10eXBlIjogImFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9VVRGLTgiLA0KICAiaG9zdCI6ICJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsDQogICJ4LWFtei1kYXRlIjogIjIwMjAwNDAxVDAwMTAxMFoiLA0KICAiWC1BbXotU2VjdXJpdHktVG9rZW4iOiAiQWdFWEFNUExFWjJsdVgyVmpFQW9hRG1Gd0xYTnZkWFJvWldGRVhBTVBMRWN3UlFJZ0FoOTdDbGpxN3dPUEw4S3N4UDNZdER1eWMvOWhBajhQaEo3RnZmMzhTZ29DSVFEaEpFWEFNUExFUHNwaW9PenRqKytwRWFnV0N2ZVpVaktFbjB6eVVoQkVYQU1QTEVqai8vLy8vLy8vLy84QkVYQU1QTEV4T0RrMk5EZ3lOemcxTlNJTW8xbVducEVTV1VvWXc0QmtLcUVGU3JtM0RYdUw4dytaYlZjNEpLakRQNHZVQ0tOUjZMZTlDOXBacDlQc1cwTm9GeTN2TEJVZEFYRVhBTVBMRU9WRzhmZVhmaUVFQSsxa2hnRksvd0V0d1IrOXpGN05hTU1Nc2UwN3dOMmdHMnRIMGVLTUVYQU1QTEVRWCtzTWJ5dFFvOGllcFA5UFpPemxac1NGYi9kUDVROGhrNllFWEFNUExFWWNLWnNUa0RBcTJ1S0ZROG1ZVVZBOUV0UW5OUmlGTEVZODNhS3ZHL3RxTFdObkdsU05WeDdTTWNmb3ZrRkRxUWFtbSs4OHkxT3d3QUVZSzdxY29jZVg2WjdHR2NhWXVJZkdwYVgyTUNDRUxlUXZaKzhXeEVnT25JZno3R1l2c1lOakxaU2FSblY0RytJTFkxRjBRTlc2NFM5TnZqK0J3RGczaHQyQ3JOdnB3alZZbGo5VTNubXhFMFVHNW5lODNMTDVoaHFNcG0yNWttTDdlblZndzJrUXptVTJpZDRJS3UwQy9XYW9EUnVPMkY1ekU2M3ZKYnhOOEFZczczMzgrNEI0SEJiNkJaNk9VZ2c5NlExNVJBNDEvZ0lxeGFWUHh5VHBEZlRVNUdmU0x4b2NkWWVuaXFxcEZNdFpHMm45ZDB1N0dzUU5jRmtOY0czcURabTR0RG84dFpidXltMGEyVmNGMkU1aEZFZ1hCYStYTEpDZlhpLzc3T3FBRWpQMHg3UWRrM0I0M3A4S0cvQmFpb1A1UnNWOHpCR3ZIMXpBZ3lQaGEyck43MC90VDEzeXJtUGQ1UVlFZnd6ZXhqS3JWNG1XSXVSZzhOVEhZU1pKVWFleUN3VG9tODBWRlVKWEcrR1lUVXl2NVcyMmFCY25vUkdpQ2lLRVlUTE9rZ1hlY2RLRlRIbWNJQWVqUTlXZWxyMGExOTZLcTg3dzVLTk1Da2NDR0Zud0JORkxtZm5icE5xVDZyVUJ4eHMzWDVudFg5ZDhIVnRTWUlOVHNHWFhNWkNKN2ZuYldhamhnL2FveDBGdEhYMjFlRjZxSUdUOGoxeitsMm9wVStnZ3dVZ2toVVVnQ0gyVGZxQmorTUxNVlZ2cGdxSnNQS3Q1ODJjYUZLQXJJRkl2Tys5UXVweExuRUgyaHowNFRNVGZuVTZiUUM2ejFidVZlN2grdE9MbmgxWVBGc0xRODhhbmliLzdUVEM4azlEc0JUcTBBU2U4UjJHYlNFc21POXFiYk13Z0VhWVVoT0t0R2V5UXNTSmRoU2s2WHhYVGhyV0w5RW53QkNYRGtJQ01xZG50QXh5eU05bldzWjRiTDlKSHFFeGdXVW1mV0NoelBGQXFuM0Y0eTg5NlVxSFRaeGxxM1dHeXBuNUhIY2VtMkhxZjNJVnhLSDFpbmhxZFZ0a3J5RWlUV3JJN1pkamJxbnFSYmwrV2d0UHRLT093ZURsQ2FSczNSMnFYY2JOZ1ZobGVNazRJV25GOEQxNjk1QWVuVTFMd0hqT0pMa0NqeGdORmlXQUZFUEg5YUVYQU1QTEV4QT09IiwNCiAgIkF1dGhvcml6YXRpb24iOiAiQVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPVhYWFhYWFhYWFhYWFhYWFhYWFgvMjAyMDA0MDEvdXMtZWFzdC0xL2FwcHN5bmMvYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWFjY2VwdDtjb250ZW50LWVuY29kaW5nO2NvbnRlbnQtdHlwZTtob3N0O3gtYW16LWRhdGU7eC1hbXotc2VjdXJpdHktdG9rZW4sIFNpZ25hdHVyZT04M0VYQU1QTEViY2MxZmUzZWU2OWY3NWNkNWViYmY0Y2I0ZjE1MGU0Zjk5Y2VjODY5ZjE0OWM1RVhBTVBMRWRjIg0KfQ"]
```

**通过标准 HTTP 标头提供标头**

在这种方法中，在与 AWS AppSync 实时端点建立 WebSocket 连接时，使用标准 HTTP 标头传输主机和其他授权信息。生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

请求标头将包括以下内容：

```
"sec-websocket-protocol" : ["graphql-ws"]
"accept": "application/json, text/javascript",
"content-encoding": "amz-1.0",
"content-type": "application/json; charset=UTF-8",
"host": "example1234567890000.appsync-api.us-east-1.amazonaws.com",
"x-amz-date": "20200401T001010Z",
"X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
"Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=83EXAMPLEbcc1fe3ee69f75cd5ebbf4cb4f150e4f99cec869f149c5EXAMPLEdc"
```

使用自定义域对请求进行签名：

```
{
  url: "https://api.example.com/graphql/connect",
  data: "{}",
  method: "POST",
  headers: {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
  }
}
```

**示例**

```
{
  "accept": "application/json, text/javascript",
  "content-encoding": "amz-1.0",
  "content-type": "application/json; charset=UTF-8",
  "host": "api.example.com",
  "x-amz-date": "20200401T001010Z",
  "X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
  "Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=83EXAMPLEbcc1fe3ee69f75cd5ebbf4cb4f150e4f99cec869f149c5EXAMPLEdc"
}
```

**带查询字符串的请求 URL**

```
wss://api.example.com/graphql?header=eyEXAMPLEHQiOiJhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L2phdmFEXAMPLEQiLCJjb250ZW50LWVuY29kaW5nIjoEXAMPLEEuMCIsImNvbnRlbnQtdHlwZSI6ImFwcGxpY2F0aW9EXAMPLE47IGNoYXJzZXQ9VVRGLTgiLCJob3N0IjoiZXhhbXBsZEXAMPLENjc4OTAwMDAuYXBwc3luYy1hcGkudXMtZWFzdC0xLmFtYEXAMPLEcy5jb20iLCJ4LWFtei1kYXRlIjoiMjAyMDA0MDFUMDAxMDEwWiIsIlgtEXAMPLElY3VyaXR5LVRva2VuIjoiQWdvSmIzSnBaMmx1WDJWakVBb2FEbUZ3TFhOdmRYUm9aV0Z6ZEMweUlrY3dSUUlnQWg5N0NsanE3d09QTDhLc3hQM1l0RHV5Yy85aEFqOFBoSjdGdmYzOFNnb0NJUURoSllKYkpsbmpQc3Bpb096dGorK3BFYWdXQ3ZlWlVqS0VuMHp5VWhCbXhpck5CUWpqLy8vLy8vLy8vLzhCRUFBYUREY3hPRGsyTkRneU56ZzFOU0lNbzFtV25wRVNXVW9ZdzRCa0txRUZTcm0zRFh1TDh3K1piVmM0SktqRFA0dlVDS05SNkxlOUM5cFpwOVBzVzBOb0Z5M3ZMQlVkQVh3dDZQSld1T1ZHOGZlWGZpRUVBKzFraGdGSy93RXR3Uis5ekY3TmFNTU1zZTA3d04yZ0cydEgwZUtNVFhuOEF3QVFYK3NNYnl0UW84aWVwUDlQWk96bFpzU0ZiL2RQNVE4aGs2WWpHVGFMMWVZY0tac1RrREFxMnVLRlE4bVlVVkE5RXRRbk5SaUZMRVk4M2FLdkcvdHFMV05uR2xTTlZ4N1NNY2ZvdmtGRHFRYW1tKzg4eTFPd3dBRVlLN3Fjb2NlWDZaN0dHY2FZdUlmR3BhWDJNQ0NFTGVRdlorOFd4RWdPbklmejdHWXZzWU5qTFpTYVJuVjRHK0lMWTFGMFFOVzY0UzlOdmorQndEZzNodDJDck52cHdqVllsajlVM25teEUwVUc1bmU4M0xMNWhocU1wbTI1a21MN2VuVmd3MmtRem1VMmlkNElLdTBDL1dhb0RSdU8yRjV6RTYzdkpieE44QVlzNzMzOCs0QjRIQmI2Qlo2T1VnZzk2UTE1UkE0MS9nSXF4YVZQeHlUcERmVFU1R2ZTTHhvY2RZZW5pcXFwRk10WkcybjlkMHU3R3NRTmNGa05jRzNxRFptNHREbzh0WmJ1eW0wYTJWY0YyRTVoRkVnWEJhK1hMSkNmWGkvNzdPcUFFalAweDdRZGszQjQzcDhLRy9CYWlvUDVSc1Y4ekJHdkgxekFneVBoYTJyTjcwL3RUMTN5cm1QZDVRWUVmd3pleGpLclY0bVdJdVJnOE5USFlTWkpVYWV5Q3dUb204MFZGVUpYRytHWVRVeXY1VzIyYUJjbm9SR2lDaUtFWVRMT2tnWGVjZEtGVEhtY0lBZWpROVdlbHIwYTE5NktxODd3NUtOTUNrY0NHRm53Qk5GTG1mbmJwTnFUNnJVQnh4czNYNW50WDlkOEhWdFNZSU5Uc0dYWE1aQ0o3Zm5iV2FqaGcvYW94MEZ0SFgyMWVGNnFJR1Q4ajF6K2wyb3BVK2dnd1Vna2hVVWdDSDJUZnFCaitNTE1WVnZwZ3FKc1BLdDU4MmNhRktBcklGSXZPKzlRdXB4TG5FSDJoejA0VE1UZm5VNmJRQzZ6MWJ1VmU3aCt0T0xuaDFZUEZzTFE4OGFuaWIvN1RUQzhrOURzQlRxMEFTZThSMkdiU0VzbU85cWJiTXdnRWFZVWhPS3RHZXlRc1NKZGhTazZYeFhUaHJXTDlFbndCQ1hEa0lDTXFkbnRBeHl5TTluV3NaNGJMOUpIcUV4Z1dVbWZXQ2h6UEZBcW4zRjR5ODk2VXFIVFp4bHEzV0d5cG41SEhjZW0ySHFmM0lWeEtIMWluaHFkVnRrcnlFaVRXckk3WmRqYnFucVJibCtXZ3RQdEtPT3dlRGxDYVJzM1IycVhjYk5nVmhsZU1rNElXbkY4RDE2OTVBZW5VMUx3SGpPSkxrQ2p4Z05GaVdBRkVQSDlhTklhcXMvWnhBPT0iLCJBdXRob3JpemF0aW9uIjoiQVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPVhYWFhYWFhYWFhYWFhYWFhYWFgvMjAxOTEwMDIvdXMtZWFzdC0xEXAMPLE5bmMvYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWFjY2VwdDtjb250ZWEXAMPLE29kaW5nO2NvbnRlbnQtdHlwZTtob3EXAMPLEW16LWRhdGU7eC1hbXotc2VjdXJpdHktdG9rZW4sIFNpZ25hdHVyZT04MzE4EXAMPLEiY2MxZmUzZWU2OWY3NWNkEXAMPLE0Y2I0ZjE1MGU0Zjk5Y2VjODY5ZjE0OWM1ZDAzNDEXAMPLEn0=&payload=e30=
```

**注意**  
一个 WebSocket 连接可以有多个订阅（即使使用不同的身份验证模式也是如此）。实现这一点的一种方法是为第一个订阅创建一个 WebSocket 连接，然后在最后一个订阅取消注册时将其关闭。您可以通过等待几秒钟再关闭 WebSocket 连接来进行优化，以防应用程序在最后一次订阅取消注册后立即订阅。以移动应用程序为例，从一个屏幕切换到另一个屏幕时，它在发生*卸载*事件时停止订阅，并在发生*挂载*事件时启动另一个订阅。

### Lambda 授权
<a name="lambda-auth"></a>

#### Lambda 授权标头
<a name="lambda-auth-list"></a>

**标头内容**
+  `"Authorization": <string>`：作为 `authorizationToken` 传递的值。
+  `"host": <string>`： AWS AppSyncGraphQL 终端节点的主机或您的自定义域名。

**示例**

```
{
    "Authorization":"M0UzQzM1MkQtMkI0Ni00OTZCLUI1NkQtMUM0MTQ0QjVBRTczCkI1REEzRTIxLTk5NzItNDJENi1BQjMwLTFCNjRFNzQ2NzlCNQo=",
    "host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
}
```

**通过查询字符串提供标头**

首先，将包含 `host` 和 `Authorization` 的 JSON 对象转换为字符串。接下来，使用 base64 编码对该字符串进行编码。生成的 base64 编码字符串作为名为的查询参数添加`header`到 WebSocket URL 中，用于与实时端点建立连接。 AWS AppSync生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoiZXlKcmFXUWlPaUpqYkc1eGIzQTVlVzVNSzA5UVlYSXJNVEpIV0VGTFNYQmllVTVXTkhoc1FqaFBWVzlZTW5NMldsZHZQU0lzSW1Gc1p5STZJbEpUTWpVMkluMC5leUp6ZFdJaU9pSmhObU5tTWpjd055MHhOamd4TFRRMU5ESXRPV1l4T0MxbE5qWTBNVGcyTmpsa016WWlMQ0psZG1WdWRGOXBaQ0k2SW1Wa016TTVNbU5rTFdOallUTXROR00yT0MxaE5EWXlMVEpsWkdJM1pUTm1ZMkZqWmlJc0luUnZhMlZ1WDNWelpTSTZJbUZqWTJWemN5SXNJbk5qYjNCbElqb2lZWGR6TG1OdloyNXBkRzh1YzJsbmJtbHVMblZ6WlhJdVlXUnRhVzRpTENKaGRYUm9YM1JwYldVaU9qRTFOamswTlRjM01UZ3NJbWx6Y3lJNkltaDBkSEJ6T2x3dlhDOWpiMmR1YVhSdkxXbGtjQzVoY0MxemIzVjBhR1ZoYzNRdE1pNWhiV0Y2YjI1aGQzTXVZMjl0WEM5aGNDMXpiM1YwYUdWaGMzUXRNbDgzT0hZMFNWWmliVkFpTENKbGVIQWlPakUxTmprME5qRXpNakFzSW1saGRDSTZNVFUyT1RRMU56Y3lNQ3dpYW5ScElqb2lOVGd6WmpobVltTXRNemsyTVMwMFl6QTRMV0poWlRBdFl6UXlZMkl4TVRNNU5EWTVJaXdpWTJ4cFpXNTBYMmxrSWpvaU0zRmxhalZsTVhabU16ZDFOM1JvWld3MGRHOTFkREprTVd3aUxDSjFjMlZ5Ym1GdFpTSTZJbVZzYjNKNllXWmxJbjAuQjRjZEp0aDNLRk5wSjZpa1ZwN2U2RFJlZTk1VjZRaS16RUUyREpIN3NIT2wyenhZaTdmLVNtRUdvaDJBRDhlbXhRUllhakJ5ei1yRTRKaDBRT3ltTjJZcy1aSWtNcFZCVFBndS1UTVdEeU9IaERVbVVqMk9QODJ5ZVozd2xaQXRyX2dNNEx6alhVWG1JX0syeUdqdVhmWFRhYTFtdlFFQkcwbVFmVmQ3U2Z3WEItamN2NFJZVmk2ajI1cWdvdzlFdzUydWZ1clBxYUstM1dBS0czMktwVjhKNC1XZWpxOHQwYy15QTdzYjhFbkI1NTFiN1RVOTN1S1JpVlZLM0U1NU5rNUFEUG9hbV9XWUU0NWkzczVxVkFQXy1Jblc3NU5Vb09DR1RzUzhZV01mYjZlY0hZSi0xai1iekEyN3phVDlWamN0WG45YnlORlptS0xwQTJMY3h3IiwiaG9zdCI6ImV4YW1wbGUxMjM0NTY3ODkwMDAwLmFwcHN5bmMtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tIn0=&payload=e30=
```

值得注意的是，除了 base64 编码的标头对象外，一个空的 JSON 对象 \$1\$1 也经过了 base64 编码，并作为单独的查询参数包含在网址中命名。`payload` WebSocket

**通过 `Sec-WebSocket-Protocol` 提供标头**

包含 `host` 和 `Authorization` 的 JSON 对象会转换为字符串，然后使用 base64Url 编码进行编码。生成的 base64Url 编码字符串的前缀为 `header-`。然后，在与 AWS AppSync 实时端点建立 WebSocket 连接时，除了`Sec-WebSocket-Protocol`标头外，还会将`graphql-ws`此前缀字符串用作新的子协议。

生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 标头包含以下值：

```
"sec-websocket-protocol" : ["graphql-ws", "header-ewogICAgImhvc3QiOiJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsCiAgICAieC1hcGkta2V5IjoiZGEyLTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Igp9"]
```

**通过标准 HTTP 标头提供标头**

在这种方法中，在与 AWS AppSync 实时端点建立 WebSocket 连接时，使用标准 HTTP 标头传输主机和授权信息。生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

请求标头将包括以下内容：

```
"sec-websocket-protocol" : ["graphql-ws"]
"Authorization":"eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJHWEFLSXBieU5WNHhsQjEXAMPLEnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyEXAMPLEiJhNmNmMjcwNy0xNjgxLTQ1NDItOWYxOC1lNjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImVkMzM5MmNkLWNjYTMtNGM2OC1hNDYyLTJlZGI3ZTNmY2FjZiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk0NTc3MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl83OHY0SVZibVAiLCJleHAiOjE1Njk0NjEzMjAsImlhdCI6MTU2OTQ1NzcyMCwianRpIjoiNTgzZjhmYmMtMzk2MS00YzA4LWJhZTAtYzQyY2IxMTM5NDY5IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1N3RoZWw0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3EXAMPLEn0.B4EXAMPLEFNpJ6ikVp7e6DRee95V6Qi-zEE2DJH7sHOl2zxYi7f-SmEGoh2AD8emxQRYajByz-rE4Jh0QOymN2Ys-ZIkMpVBTPgu-TMWDyOHhDUmUj2OP82yeZ3wlZAtr_gM4LzjXUXmI_K2yGjuXfXTaa1mvQEBG0mQfVd7SfwXB-jcv4RYVi6j25qgow9Ew52ufurPqaK-3WAKG32KpV8J4-Wejq8t0c-yA7sb8EnB551b7TU93uKRiVVK3E55Nk5ADPoam_WYE45i3s5qVAP_-InW75NUoOCGTsS8YWMfb6ecHYJ-1j-bzA27zaT9VjctXn9byNFZmEXAMPLExw",
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
```

## 实时 WebSocket 操作
<a name="real-time-websocket-operation"></a>

成功启动与的 WebSocket 握手后 AWS AppSync，客户端必须发送后续消息以 AWS AppSync 进行不同的操作。这些消息需要以下数据：
+  `type`：操作的类型。
+  `id`：订阅的唯一标识符。我们建议为此目的使用 UUID。
+  `payload`：关联的负载，取决于操作类型。

`type` 字段是唯一的必填字段；`id` 和 `payload` 字段是可选的。

### 事件序列
<a name="sequence-of-events"></a>

要成功启动、建立、注册和处理订阅请求，客户端必须执行以下序列：

1. 初始化连接 (`connection_init`)

1. 连接确认 (`connection_ack`)

1. 订阅注册 (`start`)

1. 订阅确认 (`start_ack`)

1. 处理订阅 (`data`)

1. 订阅取消注册 (`stop`)

## 连接启动消息
<a name="connection-init-message"></a>

（可选）成功握手后，客户端可以发送`connection_init`消息开始与 AWS AppSync实时端点通信。该消息是通过将 JSON 对象字符串化获得的字符串，如下所示：

```
{ "type": "connection_init" }
```

## 连接确认消息
<a name="connection-acknowledge-message"></a>

发送 `connection_init` 消息后，客户端必须等待 `connection_ack` 消息。将忽略在收到 `connection_ack` 之前发送的所有消息。该消息具体如下：

```
{
  "type": "connection_ack",
  "payload": {
    // Time in milliseconds waiting for ka message before the client should terminate the WebSocket connection
    "connectionTimeoutMs": 300000
  }
}
```

## 保持活动消息
<a name="keep-alive-message"></a>

除了连接确认消息以外，客户端还会定期收到保持活动消息。如果客户端在连接超时时间内没有收到保持活动状态的消息，则客户端应关闭连接。 AWS AppSync 继续发送这些消息并为注册的订阅提供服务，直到它自动关闭连接（24 小时后）。保持活动消息是检测信号，客户端不需要确认这些消息。

```
{ "type": "ka" }
```

## 订阅注册消息
<a name="subscription-registration-message"></a>

客户端收到`connection_ack`消息后，可以向发送订阅注册消息 AWS AppSync。这种类型的消息是字符串化 JSON 对象，其中包含以下字段：
+  `"id": <string>`：订阅的 ID。对于每个订阅，该 ID 必须是唯一的，否则，服务器返回错误，以指示订阅 ID 是重复的。
+  `"type": "start"`：常量 `<string>` 参数。
+  `"payload": <Object>`：包含与订阅相关的信息的对象。
  +  `"data": <string>`：包含 GraphQL 查询和变量的字符串化 JSON 对象。
    +  `"query": <string>`：GraphQL 操作。
    +  `"variables": <Object>`：包含查询变量的对象。
  +  `"extensions": <Object>`：包含授权对象的对象。
+  `"authorization": <Object>`：包含授权所需的字段的对象。

### 订阅注册的授权对象
<a name="authorization-object-for-subscription-registration"></a>

[基于 AWS AppSync API 授权模式的标头参数格式](#header-parameter-format-based-on-appsync-api-authorization-mode)一节中的相同规则适用于授权对象。唯一的例外是 IAM，其中的 SigV4 签名信息略有不同。有关详细信息，请参阅 IAM 示例。

使用 Amazon Cognito 用户池的示例：

```
{
  "id": "ee849ef0-cf23-4cb8-9fcb-152ae4fd1e69",
  "payload": {
    "data": "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
      "extensions": {
        "authorization": {
          "Authorization": "eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJEXAMPLEBieU5WNHhsQjhPVW9YMnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyJzdWIiOiJhNmNmMjcwNy0xNjgxLTQ1NDItEXAMPLENjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImU3YWVmMzEyLWUEXAMPLEY0Zi04YjlhLTRjMWY5M2Q5ZTQ2OCIsInRva2VuX3VzZSI6ImFjY2VzcyIsIEXAMPLEIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk2MTgzMzgsImlzcyI6Imh0dEXAMPLEXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zbEXAMPLEc3QtMl83OHY0SVZibVAiLCJleHAiOjE1NzAyNTQ3NTUsImlhdCI6MTU3MDI1MTE1NSwianRpIjoiMmIEXAMPLEktZTVkMi00ZDhkLWJiYjItNjA0YWI4MDEwOTg3IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1EXAMPLE0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3J6YWZlIn0.CT-qTCtrYeboUJ4luRSTPXaNewNeEXAMPLE14C6sfg05tO0fOMpiUwj9k19gtNCCMqoSsjtQoUweFnH4JYa5EXAMPLEVxOyQEQ4G7jQrt5Ks6STn53vuseR3zRW9snWgwz7t3ZmQU-RWvW7yQU3sNQRLEXAMPLEcd0yufBiCYs3dfQxTTdvR1B6Wz6CD78lfNeKqfzzUn2beMoup2h6EXAMPLE4ow8cUPUPvG0DzRtHNMbWskjPanu7OuoZ8iFO_Eot9kTtAlVKYoNbWkZhkD8dxutyoU4RSH5JoLAnrGF5c8iKgv0B2dfEXAMPLEIihxaZVJ9w9w48S4EXAMPLEcA",
          "host": "example1234567890000.appsync-api.us-east-1.amazonaws.com"
         }
      }
  },
  "type": "start"
}
```

使用 IAM 的示例：

```
{
  "id": "eEXAMPLE-cf23-1234-5678-152EXAMPLE69",
  "payload": {
    "data": "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
    "extensions": {
      "authorization": {
        "accept": "application/json, text/javascript",
        "content-type": "application/json; charset=UTF-8",
        "X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
        "Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=b90131a61a7c4318e1c35ead5dbfdeb46339a7585bbdbeceeaff51f4022eb1fd",
        "content-encoding": "amz-1.0",
        "host": "example1234567890000.appsync-api.us-east-1.amazonaws.com",
        "x-amz-date": "20200401T001010Z"
      }
    }
  },
  "type": "start"
}
```

使用自定义域名的示例：

```
{
  "id": "key-cf23-4cb8-9fcb-152ae4fd1e69",
  "payload": {
    "data": "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
      "extensions": {
        "authorization": {
          "x-api-key": "da2-12345678901234567890123456",
          "host": "api.example.com"
         }
      }
  },
  "type": "start"
}
```

SigV4 签名不需要将 `/connect` 附加到 URL 中，并使用 JSON 字符串化 GraphQL 操作替换 `data`。以下是一个 SigV4 签名请求示例：

```
{
  url: "https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql",
  data: "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
  method: "POST",
  headers: {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
  }
}
```

## 订阅确认消息
<a name="subscription-acknowledge-message"></a>

发送订阅开始消息后，客户端应等待 AWS AppSync发送`start_ack`消息。`start_ack` 消息表示订阅成功。

订阅确认示例：

```
{
  "type": "start_ack",
  "id": "eEXAMPLE-cf23-1234-5678-152EXAMPLE69"
}
```

## 错误消息
<a name="error-message"></a>

如果连接启动或订阅注册失败，或者从服务器中终止订阅，服务器将向客户端发送错误消息。如果在连接初始化期间发生错误，则服务器会关闭连接。
+  `"type": "error"`：常量 `<string>` 参数。
+  `"id": <string>`：注册的相应订阅的 ID（如果相关）。
+  `"payload" <Object>`：包含相应错误信息的对象。

示例：

```
{
  "type": "error",
  "payload": {
    "errors": [
      {
        "errorType": "LimitExceededError",
        "message": "Rate limit exceeded"
      }
    ]
  }
}
```

## 处理数据消息
<a name="processing-data-messages"></a>

当客户端提交突变时，会 AWS AppSync 识别所有对此变更感兴趣的订阅者，并使用订阅操作中的相应订阅向每个订阅`id`者`"start"`发送`"type":"data"`消息。客户端应跟踪它发送的订阅 `id`，这样，在它收到数据消息时，客户端可以将其与相应的订阅进行匹配。
+  `"type": "data"`：常量 `<string>` 参数。
+  `"id": <string>`：注册的相应订阅的 ID。
+  `"payload" <Object>`：包含订阅信息的对象。

示例：

```
{
  "type": "data",
  "id": "ee849ef0-cf23-4cb8-9fcb-152ae4fd1e69",
  "payload": {
    "data": {
      "onCreateMessage": {
        "__typename": "Message",
        "message": "test"
      }
    }
  }
}
```

## 订阅取消注册消息
<a name="subscription-unregistration-message"></a>

在应用程序希望停止侦听订阅事件时，客户端应发送包含以下字符串化 JSON 对象的消息：
+  `"type": "stop"`：常量 `<string>` 参数。
+  `"id": <string>`：要取消注册的订阅的 ID。

示例：

```
{
  "type":"stop",
  "id":"ee849ef0-cf23-4cb8-9fcb-152ae4fd1e69"
}
```

AWS AppSync 使用以下字符串化的 JSON 对象发回确认消息：
+  `"type": "complete"`：常量 `<string>` 参数。
+  `"id": <string>`：取消注册的订阅的 ID。

在客户端收到确认消息后，它不会接收该特定订阅的更多消息。

示例：

```
{
  "type":"complete",
  "id":"eEXAMPLE-cf23-1234-5678-152EXAMPLE69"
}
```

## 断开连接 WebSocket
<a name="disconnecting-the-websocket"></a>

在断开连接之前，为避免数据丢失，客户端应具有必要的逻辑来检查当前是否未通过 WebSocket连接进行任何操作。在断开与的连接之前，应取消注册所有订阅。 WebSocket