为 Express 定义 Websocket 路由
Posted
技术标签:
【中文标题】为 Express 定义 Websocket 路由【英文标题】:Defining Websocket routes for Express 【发布时间】:2021-10-29 18:23:58 【问题描述】:如何在 ExpressJS 应用程序中使用 ws 库为 Websocket 定义路由?并行设置两层非常容易,但是 Websocket 层将无法从 ExpressJS 中间件(例如身份验证)中受益。我能找到的唯一实现是express-ws,由于不是最新的,它存在严重的错误,并且严重依赖猴子补丁才能工作。
【问题讨论】:
【参考方案1】:部分修改自此answer。修改您的入口文件以包含以下内容:
/* index.ts */
import http from 'http';
import express from 'express';
import exampleRouter from './exampleRouter';
// set up express and create a http server listen for websocket requests on the same port
const app = express();
const server = http.createServer(app);
// listen for websocket requests, which are simple HTTP GET requests with an upgrade header
// NOTE: this must occur BEFORE other middleware is set up if you want the additional ws.handled functionality to close unhandled requests
server.on('upgrade', (req: Request & ws: socket: Socket, head: Buffer, handled: Boolean , socket: Socket, head: Buffer) =>
// create a dummy response to pass the request into express
const res = new http.ServerResponse(req);
// assign socket and head to a new field in the request object
// optional **handled** field lets us know if there a route processed the websocket request, else we terminate it later on
req.ws = socket, head, handled: false ;
// have Express process the request
app(req, res);
);
/* whatever Express middlewares you want here, such as authentication */
app.use('/example', exampleRouter);
// set up a middleware to destroy unhandled websocket requests and returns a 403
// NOTE: this must occur AFTER your other middlewares but BEFORE the server starts listening for requests
app.use((req: Request & ws?: socket: Socket, head: Buffer, handled: Boolean , res: Response, next: NextFunction): void =>
if (req.ws && req.ws.handled === false)
req.ws.socket.destroy();
res.status(404).json('404: Websocket route not found');
next();
);
const port = process.env.PORT || 8080;
server.listen(port);
具有 ws 功能的 Express Router 示例,但可以提取逻辑以用于一次性
/* RouterWithWebSockets.ts */
// this is just a simple abstraction implementation so the you can set up multiple ws routes with the same router
// without having to rewrite the WSS code or monkeypatch the function into the Express Router directly
import express from 'express';
import WebSocketServer, WebSocket from 'ws';
class RouterWithWebSockets
router;
constructor(router = express.Router())
this.router = router;
ws = (path: string, callback: (ws: WebSocket) => void, ...middleware: any): void =>
// set up a new WSS with the provided path/route to handle websockets
const wss = new WebSocketServer(
noServer: true,
path,
);
this.router.get(path, ...middleware, (req: any, res, next) =>
// just an extra check to deny upgrade requests if the path/route does not match
// you can process this route as a regular HTTP GET request if it's not a websocket upgrade request by replacing the next()
if (!req.headers.upgrade || path !== req.url)
next();
else
req.ws.handled = true;
wss.handleUpgrade(req, req.ws.socket, req.ws.head, (ws: WebSocket) =>
callback(ws);
);
);
;
export default RouterWithWebSockets;
最后,这是一个带有 Websocket 路由的示例路由器
/* exampleRouter.ts */
const routerWithWebSockets = new RouterWithWebSockets();
routerWithWebSockets.router.get('/nonWSRoute', doSomething1); // processed as HTTP GET request
routerWithWebSockets.router.get('/wsRoute1', doSomething2); // processed as HTTP GET request
routerWithWebSockets.ws('/wsRoute1', (ws) => doSomethingWithWS1); // processed as Websocket upgrade request
routerWithWebSockets.ws('/wsRoute2', (ws) => doSomethingWithWS2); // processed as Websocket upgrade request
export default routerWithWebSockets.router;
【讨论】:
以上是关于为 Express 定义 Websocket 路由的主要内容,如果未能解决你的问题,请参考以下文章