Azure Web PubSub 的 WebSocket 客户端协议
客户端通过标准 WebSocket 协议连接到 Azure Web PubSub。
服务终结点
Web PubSub 服务提供两种类型的终结点供客户端与之连接:
/client/hubs/{hub}
/client/?hub={hub}
{hub}
是必需参数,充当各种应用程序的隔离项。 可以在路径或查询中设置它。
授权
客户端使用 JSON Web 令牌 (JWT) 连接到服务。 可以将令牌以 /client/?hub={hub}&access_token={token}
形式置于查询字符串中,或以 Authorization: Bearer {token}
形式置于 Authorization
标头中。
下面是常规授权工作流:
- 客户端与应用程序服务器协商。 应用程序服务器包含的授权中间件可处理客户端请求,并对 JWT 进行签名,以使客户端连接到服务。
- 应用程序服务器将 JWT 和服务 URL 返回到客户端。
- 客户端尝试使用从应用程序服务器返回的 URL 和 JWT 令牌连接到 Web PubSub 服务。
支持的声明
还可以通过在 JWT 令牌中指定特殊声明,在生成访问令牌时为客户端连接配置属性:
说明 | 声明类型 | 声明值 | 说明 |
---|---|---|---|
用于客户端连接的 userId |
sub |
userId | 只允许一个 sub 声明。 |
令牌的生存期 | exp |
过期时间 | exp (过期时间)声明指定只能在哪个时间(含)之前接受令牌的处理。 |
客户端连接最初具有的权限 | role |
权限中定义的角色值 | 如果客户端具有多个权限,请指定多个 role 声明。 |
客户端连接连接到 Azure Web PubSub 后加入的初始组 | group |
要加入的组 | 如果客户端加入多个组,请指定多个 group 声明。 |
还可以将自定义声明添加到访问令牌中,这些值将保留为连接上游请求正文中的 claims
属性。
服务器 SDK 提供用于为客户端生成访问令牌的 API。
简单的 WebSocket 客户端
顾名思义,简单的 WebSocket 客户端就是一个简单的 WebSocket 连接。 它还可以有自己的自定义子协议。
例如,在 JavaScript 中,可以使用以下代码创建简单的 WebSocket 客户端:
// simple WebSocket client1
var client1 = new WebSocket('wss://test.webpubsub.azure.cn/client/hubs/hub1');
// simple WebSocket client2 with some custom subprotocol
var client2 = new WebSocket('wss://test.webpubsub.azure.cn/client/hubs/hub1', 'custom.subprotocol')
PubSub WebSocket 客户端
PubSub WebSocket 客户端是使用由 Azure Web PubSub 服务定义的子协议的 WebSocket 客户端:
json.webpubsub.azure.v1
protobuf.webpubsub.azure.v1
通过使用服务支持的子协议,PubSub WebSocket 客户端在有相关权限时可以直接将消息发布到组。
json.webpubsub.azure.v1
子协议
创建 PubSub WebSocket 客户端
var pubsubClient = new WebSocket('wss://test.webpubsub.azure.cn/client/hubs/hub1', 'json.webpubsub.azure.v1');
直接从客户端加入组
let ackId = 0;
pubsubClient.send(
JSON.stringify({
type: 'joinGroup',
group: 'group1',
ackId: ++ackId
}));
直接从客户端向组发送消息
let ackId = 0;
pubsubClient.send(
JSON.stringify({
type: 'sendToGroup',
group: 'group1',
ackId: ++ackId,
dataType: "json",
data: {
"hello": "world"
}
}));
protobuf.webpubsub.azure.v1
子协议
协议缓冲区 (protobuf) 是一种语言中性、平台中性的基于二进制的协议,可简化二进制数据的发送。 Protobuf 提供的工具可为 Java、Python、Objective-C、C# 和 C++ 等多种语言生成客户端。
例如,在 JavaScript 中,可以通过以下代码创建使用 protobuf 子协议的 PubSub WebSocket 客户端:
// PubSub WebSocket client
var pubsub = new WebSocket('wss://test.webpubsub.azure.cn/client/hubs/hub1', 'protobuf.webpubsub.azure.v1');
AckId 和 Ack 响应
PubSub WebSocket 客户端支持 joinGroup
、leaveGroup
、sendToGroup
和 event
消息的 ackId
属性。 使用 ackId
时,你可以在你的请求被处理时收到确认响应消息。 可以在即发即弃场景中选择省略 ackId
。 本文介绍了指定或不指定 ackId
时的行为差异。
未指定 ackId
时的行为
如果未指定 ackId
,则是即发即弃。 即使中转消息时出现错误,你也无法收到通知。
指定 ackId
时的行为
幂等发布
ackId
是 uint64 数字,在具有相同连接 ID 的客户端中应是唯一的。 Web PubSub 服务会记录 ackId
,具有相同 ackId
的消息将被视为同一消息。 服务拒绝多次中转同一消息,这有助于重试时避免重复的消息。 例如,如果客户端发送 ackId=5
的消息并且无法接收 ackId=5
的确认响应,那么客户端将再次重试并发送相同的消息。 在某些情况下,消息已中转,并且确认响应由于某种原因已丢失。 服务会拒绝重试并用 Duplicate
原因响应确认响应。
确认响应
Web PubSub 服务将为具有 ackId
的每个请求发送确认响应。
格式:
{
"type": "ack",
"ackId": 1, // The ack id for the request to ack
"success": false, // true or false
"error": {
"name": "Forbidden|InternalServerError|Duplicate",
"message": "<error_detail>"
}
}
ackId
与请求相关联。success
是一个布尔值,指示服务是否成功处理了请求。 如果为false
,客户端需要检查error
。error
仅在success
为false
时存在,并且客户端针对不同的name
应具有不同的逻辑。 你应假设将来可能有更多类型的name
。Forbidden
:客户端没有请求权限。 需要向客户端添加相关角色。InternalServerError
:服务中出现了一些内部错误。 需要重试。Duplicate
:服务已处理ackId
相同的消息。
权限
你可能已在前面的 PubSub WebSocket 客户端介绍中注意到,一个客户端只有在获得授权时才能发布到其他客户端。 当客户端建立连接或在连接的生存期内时,可以对其授予权限。
角色 | 权限 |
---|---|
未指定 | 客户端可以发送事件请求。 |
webpubsub.joinLeaveGroup |
客户端可以加入或退出任何组。 |
webpubsub.sendToGroup |
客户端可以向任何组发布消息。 |
webpubsub.joinLeaveGroup.<group> |
客户端可以加入或退出 <group> 组。 |
webpubsub.sendToGroup.<group> |
客户端可以向 <group> 组发布消息。 |
可以通过多种方式授予客户端的权限:
1. 生成访问令牌时将角色分配给客户端
客户端可以使用 JWT 令牌连接到服务。 令牌有效负载可以携带客户端 role
等信息。 将 JWT 令牌签名到客户端时,可以通过授予客户端特定角色向客户端授予权限。
例如,我们对有权将消息发送到 group1
和 group2
的 JWT 令牌进行签名:
let token = await serviceClient.getClientAccessToken({
roles: [ "webpubsub.sendToGroup.group1", "webpubsub.sendToGroup.group2" ]
});
2. 使用 connect
事件处理程序将角色分配给客户端
还可以在注册 connect
事件处理程序时设置客户端的角色,并且上游事件处理程序可以在处理 connect
事件时将客户端的 roles
返回给 Web PubSub 服务。
例如,在 JavaScript 中,可以配置 handleConnect
事件来这样做:
let handler = new WebPubSubEventHandler("hub1", {
handleConnect: (req, res) => {
// auth the connection and set the userId of the connection
res.success({
roles: [ "webpubsub.sendToGroup.group1", "webpubsub.sendToGroup.group2" ]
});
},
});
3. 在运行时期间通过 REST API 或服务器 SDK 将角色分配给客户端
let service = new WebPubSubServiceClient("<your_connection_string>", "test-hub");
await service.grantPermission("<connection_id>", "joinLeaveGroup", { targetName: "group1" });
后续步骤
使用这些资源开始生成自己的应用程序: