【问题标题】:Defining Websocket routes for Express为 Express 定义 Websocket 路由
【发布时间】:2021-10-29 18:23:58
【问题描述】:

如何在 ExpressJS 应用程序中使用 ws 库为 Websocket 定义路由?并行设置两层非常容易,但是 Websocket 层将无法从 ExpressJS 中间件(例如身份验证)中受益。我能找到的唯一实现是express-ws,由于不是最新的,它存在严重的错误,并且严重依赖猴子补丁才能工作。

【问题讨论】:

    标签: express websocket


    【解决方案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;
    

    【讨论】:

      猜你喜欢
      • 2017-06-01
      • 1970-01-01
      • 2014-04-21
      • 2019-05-07
      • 2023-03-09
      • 1970-01-01
      • 2016-04-11
      • 2015-01-07
      • 2018-11-30
      相关资源
      最近更新 更多