升级WebSocket连接时如何获取Express风格的request请求对象
解决思路很简单:upgrade事件拿到的原生request没有经过Express中间件处理,你只需要在处理WebSocket升级前,手动让这个原生request执行一遍你需要的Express中间件,等用户信息挂载完成后再走升级逻辑即可。
具体实现步骤
- 提前抽离需要用到的、会向request挂载用户信息的中间件(比如会话中间件、权限校验中间件等),同时保持原来的Express注册逻辑不变:
// 示例为session + passport鉴权的场景,可替换为你自己的业务中间件 const sessionMiddleware = session({ secret: "自定义会话密钥", resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === "production" } }) const passportInit = passport.initialize() const passportSession = passport.session() // 原有Express注册中间件的逻辑不变 app.use(sessionMiddleware) app.use(passportInit) app.use(passportSession)
- 修改
upgrade事件逻辑,在执行handleUpgrade前先运行上述中间件处理原生request,校验通过后再完成升级,同时可以直接把用户信息挂载到ws实例上方便后续使用:
server.on('upgrade', (request, socket: any, head) => { // 按顺序执行需要的中间件,第二个参数传空对象占位response即可 sessionMiddleware(request, {} as any, () => { passportInit(request, {} as any, () => { passportSession(request, {} as any, () => { // 此时request上已经挂载了用户信息,可做权限校验 if (!request.user) { // 无权限直接拒绝连接 socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n') socket.destroy() return } wss.handleUpgrade(request, socket, head, (ws: WebSocket) => { // 将用户信息挂载到ws实例,后续收发消息时可直接读取 ws.user = request.user wss.emit('connection', ws, request) }) }) }) }) })
如果你用的是自定义的鉴权中间件,直接替换上面的session、passport相关中间件即可,逻辑完全一致。
优化方案(避免回调嵌套)
可以把中间件执行封装成Promise,用async/await简化代码:
// 封装中间件执行函数 function runMiddleware(req: any, res: any, middleware: Function) { return new Promise((resolve, reject) => { middleware(req, res, (result: any) => { if (result instanceof Error) return reject(result) resolve(result) }) }) } // 改造后的upgrade逻辑 server.on('upgrade', async (request, socket: any, head) => { try { const emptyRes = {} as any await runMiddleware(request, emptyRes, sessionMiddleware) await runMiddleware(request, emptyRes, passportInit) await runMiddleware(request, emptyRes, passportSession) if (!request.user) { socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n') socket.destroy() return } wss.handleUpgrade(request, socket, head, (ws: WebSocket) => { ws.user = request.user wss.emit('connection', ws, request) }) } catch (err) { socket.write('HTTP/1.1 500 Internal Server Error\r\n\r\n') socket.destroy() } })
内容的提问来源于stack exchange,提问作者jdoe8227
相关产品推荐
相关产品推荐

