我正在使用Typescript在Express.js中编写REST API。该API基于随机生成的“会议室令牌”,该字符串是指会话数据的特定实例的字符串。会议室令牌可以在多个客户端之间共享-通常是让一个用户创建一个新会议室,然后他们复制粘贴URL以与包括该会议室令牌的朋友/同事共享。
在此API中,我需要支持服务器发送事件(SSE)。看来,具有Typescript类型的Express的唯一SSE中间件是ExpreSSE。
因此,我想做的是实施“广播的” SSE(我用术语“广播”来表示;我知道在网络层实际上是一对一的),每个事件仅针对那些具有订阅了特定的房间令牌。
为了说明这一点,请想象下面的对话:
/api/subscribeToChangeNotification
呼叫{token: '12345'}
。/api/subscribeToChangeNotification
呼叫{token: '6789'}
。/api/subscribeToChangeNotification
调用{token: '12345'}
。Hub
对象,其中:Hub
中间件为sse
。这不是我想要的,因为那样的话,“广播”将只发给一个单独的客户端,而不是每个客户端订阅给定令牌。方法2涉及设置全局Hub
并将其传递到sseHub
中间件。subscribeToChangeNotification
时,该方法可让我根据请求的有效负载指定要使用的集线器。我该怎么办?middleware
函数的第一行调用我特定于应用程序的会话全局数据(Map
),该数据为每个不同的房间令牌创建并保留对新Hub
的引用。 middleware
函数的其余部分只是从ExpreSSE的源代码的sseHub()
复制粘贴而来。然后,我将SSE API中的dynamicSseHub()
用作中间件。/**
* SSE middleware that configures an Express response for an SSE session, installs `sse.*` functions on the Response
* object, as well as the `sse.broadcast.*` variants.
*
* @param options An ISseMiddlewareOptions to configure the middleware's behaviour.
*/
function dynamicSseHub(options: Partial<ISseMiddlewareOptions> = {}): Handler {
function middleware(req: Request, res: ISseResponse, next: NextFunction): void {
let hub : IHub = sessions.get(req.body.toString()).hub;
//=> Register the SSE functions of that client on the hub
hub.register(res.sse);
//=> Unregister the user from the hub when its connection gets closed (close=client, finish=server)
res.once('close', () => hub.unregister(res.sse));
res.once('finish', () => hub.unregister(res.sse));
//=> Make hub's functions available on the response
(res as ISseHubResponse).sse.broadcast = {
data: hub.data.bind(hub),
event: hub.event.bind(hub),
comment: hub.comment.bind(hub),
};
//=> Done
next();
}
return compose(sse(options), middleware);
}