使用 HTTP 协议的 Azure Web PubSub 事件处理程序的 CloudEvents 扩展

Web PubSub 服务使用 CloudEvents HTTP 协议绑定将客户端事件传送到上游 Webhook。

从 Web PubSub 服务发送到服务器的数据一律采用 CloudEvents binary 格式。

Webhook 验证

Webhook 验证遵循 CloudEvents。 请求的标头中始终包含 WebHook-Request-Origin: xxx.webpubsub.azure.cn

当且仅当传送目标允许传送事件时,在答复请求时必须包含 WebHook-Allowed-Origin 标头,例如:

WebHook-Allowed-Origin: *

或:

WebHook-Allowed-Origin: xxx.webpubsub.azure.cn

目前,不支持 WebHook-Request-RateWebHook-Request-Callback

Web PubSub CloudEvents 属性扩展

另请注意,HTTP 规范现在遵循类似的模式,不再建议扩展 HTTP 标头以 X- 作为前缀。

此扩展插件为它生成的每个事件定义 Web PubSub 使用的属性。

属性

名称 Type 说明 示例
userId string 连接通过身份验证的用户
hub string 连接所属于的中心
connectionId string connectionId 对于客户端连接是唯一的
eventName string 不带前缀的事件名称
subprotocol string 客户端使用的子协议(如果有)
connectionState string 定义连接的状态。 你可以使用相同的响应头来重置状态值。 不允许使用多个 connectionState 头。 如果字符串值内部包含复杂字符,则可对其进行 base64 编码,例如,使用此属性通过 base64(jsonString) 传递复杂对象。
signature string 上游 Webhook 的签名,用于验证传入请求是否来自预期来源。 该服务在计算值时使用主访问密钥和辅助访问密钥作为 HMAC 密钥:Hex_encoded(HMAC_SHA256(accessKey, connectionId))。 上游应在处理请求之前检查请求是否有效。

事件

有两种类型的事件。 一种是阻塞事件,服务等待这种事件的响应以继续后续步骤。 一种是非阻塞事件,服务在处理下一条消息之前,不会等待此类事件的响应。

系统 connect 事件

  • ce-type: azure.webpubsub.sys.connect
  • Content-Type: application/json

请求格式:

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.cn
Content-Type: application/json; charset=utf-8
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.sys.connect
ce-source: /hubs/{hub}/client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub}
ce-eventName: connect

{
    "claims": {},
    "query": {},
    "headers": {},
    "subprotocols": [],
    "clientCertificates": [
        {
            "thumbprint": "ABC"
        }
    ]
}

成功响应格式:

  • ce-connectionState 标头:如果此标头存在,则此连接的连接状态将更新为标头的值。 只有阻塞事件可以更新连接状态。 下面的示例使用 base64 编码的 JSON 字符串存储连接的复杂状态。

  • 状态代码:

    • 204:成功,无内容。
    • 200:成功后,内容应为 JSON 格式,允许以下属性:
      • subprotocols

        connect 事件将子协议和身份验证信息从客户端转发到 Upstream。 Web PubSub 服务使用状态码来确定请求是否将升级为 WebSocket 协议。

        如果请求包含 subprotocols 属性,则服务器应返回它支持的一个子协议。 如果服务器不想使用任何子协议,则不应在响应中发送 subprotocol 属性。 发送空白标头无效

      • userId: {auth-ed user ID}

        由于服务允许匿名连接,因此 connect 事件负责向服务告知客户端连接的用户 ID。 服务会从响应有效负载 userId 中读取用户 ID(如果存在)。 如果从请求声明和 connect 事件的响应有效负载中均无法读取用户 ID,连接会被删除。

      • groups: {groups to join}

        此属性为用户提供了一种将此连接添加到一个或多个组的简便方法。 这样,在将此连接添加到某个组时无需再进行另一次调用。

      • roles: {roles the client has}

        该属性为上游 Webhook 提供了一种授权客户端的方法。 有不同的角色可以为 PubSub WebSocket 客户端授予初始权限。 有关权限的详细信息,请参阅客户端权限

HTTP/1.1 200 OK
ce-connectionState: eyJrZXkiOiJhIn0=

{
    "groups": [],
    "userId": "",
    "roles": [],
    "subprotocol": ""
}

错误响应格式:

  • 4xx:错误,来自 Upstream 的响应将返回为客户端请求的响应。
HTTP/1.1 401 Unauthorized

系统 connected 事件

当客户端完成 WebSocket 握手并成功连接时,服务将调用 Upstream。

  • ce-type: azure.webpubsub.sys.connected
  • Content-Type: application/json
  • ce-connectionState: eyJrZXkiOiJhIn0=

请求正文为空 JSON。

请求格式:

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.cn
Content-Type: application/json; charset=utf-8
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.sys.connected
ce-source: /hubs/{hub}/client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub}
ce-eventName: connected
ce-subprotocol: abc
ce-connectionState: eyJrZXkiOiJhIn0=

{}

响应格式:

2xx:成功响应。

connected 是一个异步事件,当响应状态代码表示未成功时,服务将记录一条错误。

HTTP/1.1 200 OK

系统 disconnected 事件

如果 connect 事件返回了 2xx 状态代码,则在客户端请求完成时,将始终会触发 disconnected 事件。

  • ce-type: azure.webpubsub.sys.disconnected
  • Content-Type: application/json

请求格式:

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.cn
Content-Type: application/json; charset=utf-8
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.sys.disconnected
ce-source: /hubs/{hub}/client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub}
ce-eventName: disconnected
ce-subprotocol: abc
ce-connectionState: eyJrZXkiOiJhIn0=

{
    "reason": "{Reason}"
}

  • reason

    reason 描述了客户端断开连接的原因。

响应格式:

2xx:成功响应。

disconnected 是一个异步事件,当响应状态代码表示未成功时,服务将记录一条错误。

HTTP/1.1 200 OK

简单 WebSocket 客户端的用户事件 message

该服务针对每个 WebSocket 消息帧在上游调用事件处理程序。

  • ce-type: azure.webpubsub.user.message
  • Content-Type: application/octet-stream 表示二进制帧;text/plain 表示文本帧;

UserPayload 是客户端发送的内容。

请求格式:

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.cn
Content-Type: application/octet-stream | text/plain | application/json
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.user.message
ce-source: /hubs/{hub}/client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub}
ce-eventName: message
ce-connectionState: eyJrZXkiOiJhIn0=

UserPayload

成功响应格式

  • 状态代码
    • 204:成功,无内容。
    • 200:成功,UserResponsePayload 的格式取决于响应的 Content-Type
  • Content-Type: application/octet-stream 表示二进制帧;text/plain 表示文本帧;
  • Content-Type 标头:application/octet-stream 表示二进制帧;text/plain 表示文本帧;
  • ce-connectionState 标头:如果此标头存在,则此连接的连接状态将更新为标头的值。 只有阻塞事件可以更新连接状态。 下面的示例使用 base64 编码的 JSON 字符串存储连接的复杂状态。

Content-Typeapplication/octet-stream 时,服务使用 binary WebSocket 帧向客户端发送 UserResponsePayload。 当 Content-Typetext/plain 时,服务使用 text WebSocket 帧向客户端发送 UserResponsePayload

HTTP/1.1 200 OK
Content-Type: application/octet-stream (for binary frame) or text/plain (for text frame)
Content-Length: nnnn
ce-connectionState: eyJrZXkiOiJhIn0=

UserResponsePayload

错误响应格式

当状态代码表示未成功时,将其被视为错误响应。 如果 message 响应状态代码表示未成功时,连接将被删除。

PubSub WebSocket 客户端的用户自定义事件 {custom_event}

服务针对每个有效的自定义事件消息调用事件处理程序 Webhook。

案例 1:发送包含文本数据的事件:

{
    "type": "event",
    "event": "<event_name>",
    "dataType" : "text",
    "data": "text data"
}

上游事件处理程序的内容如下所示,对于 dataType=text,CloudEvents HTTP 请求的 Content-Typetext/plain

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.cn
Content-Type: text/plain
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.user.<event_name>
ce-source: /client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub_name}
ce-eventName: <event_name>
ce-subprotocol: json.webpubsub.azure.v1
ce-connectionState: eyJrZXkiOiJhIn0=

text data

案例 2:发送包含 JSON 数据的事件:

{
    "type": "event",
    "event": "<event_name>",
    "dataType" : "json",
    "data": {
        "hello": "world"
    },
}

上游事件处理程序的内容如下所示,对于 dataType=json,CloudEvents HTTP 请求的 Content-Typeapplication/json

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.cn
Content-Type: application/json
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.user.<event_name>
ce-source: /client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub_name}
ce-eventName: <event_name>
ce-subprotocol: json.webpubsub.azure.v1
ce-connectionState: eyJrZXkiOiJhIn0=

{
    "hello": "world"
}

案例 3:发送包含二进制数据的事件:

{
    "type": "event",
    "event": "<event_name>",
    "dataType" : "binary",
    "data": "aGVsbG8gd29ybGQ=" // base64 encoded binary
}

上游事件处理程序的内容如下所示,对于 dataType=binary,CloudEvents HTTP 请求的 Content-Typeapplication/octet-stream

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.cn
Content-Type: application/octet-stream
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.user.<event_name>
ce-source: /client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub_name}
ce-eventName: <event_name>
ce-subprotocol: json.webpubsub.azure.v1

<binary data>

成功响应格式

HTTP/1.1 200 OK
Content-Type: application/octet-stream | text/plain | application/json
Content-Length: nnnn

UserResponsePayload
  • 状态代码
    • 204:成功,无内容。
    • 200:成功,发送到 PubSub WebSocket 客户端的数据依赖于 Content-Type
  • ce-connectionState 标头:如果此标头存在,则此连接的连接状态将更新为标头的值。 只有阻塞事件可以更新连接状态。 下面的示例使用 base64 编码的 JSON 字符串存储连接的复杂状态。
  • Content-Type 标头为 application/octet-stream 时,服务在将 UserResponsePayload 发送回客户端时将使用 dataType 作为 binary,并且有效负载采用 base64 编码。 示例响应:
    {
        "type": "message",
        "from": "server",
        "dataType": "binary",
        "data" : "aGVsbG8gd29ybGQ="
    }
    
  • Content-Typetext/plain 时,服务在将 UserResponsePayload 发送到客户端时将使用 dataType 作为 text,并且有效负载为字符串。
  • Content-Typeapplication/json 时,服务在将 UserResponsePayload 发送到客户端时将使用 dataType=json,后者以 data 值令牌作为响应有效负载正文。

错误响应格式

当状态代码表示未成功时,将其被视为错误响应。 如果 {custom_event} 响应状态代码表示未成功时,连接将被删除。

后续步骤

使用这些资源开始生成自己的应用程序: