【问题标题】:express.js 4 and sockets with express routerexpress.js 4 和带有快速路由器的套接字
【发布时间】:2015-06-02 19:04:48
【问题描述】:

我正在尝试使用 express.js 4 创建一个非常简单的节点 API,但我需要一些“实时”事件,为此我添加了 socket.io。我对两者都很陌生,所以我可能会遗漏一些基本的东西,但我找不到好的文档/tuts。

在 express 应用程序(使用 express 生成器创建)中,基于我阅读的简单示例和项目文档,我有类似的东西。这工作正常,从客户端应用程序,我可以发送/接收套接字事件:

var express = require('express');
var path = require('path');
var logger = require('morgan');
var api = require('./routes/api');
var app = express();
var io = require('socket.io').listen(app.listen(3000));

app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/api', api);

io.sockets.on('connection', function (socket) {
    console.log('client connect');
    socket.on('echo', function (data) {
        io.sockets.emit('message', data);
    });
});


// error handlers omitted

module.exports = app;

但我想使用我的 API 路由中的套接字(在我上面“需要”的 ./routes/api.js 文件中)。例如,有人可能使用 API 来 PUT/POST 资源,我希望该广播到连接的 socket.io 客户端。

我看不到如何使用“io”变量或组织当前在快速路由内的io.sockets.on('connection' ... 函数中的代码。这是./routes/api.js 文件:

var express = require('express');
var router = express.Router();
var io = ???;

router.put('/foo', function(req, res) {
    /* 
      do stuff to update the foo resource 
      ...
     */

    // now broadcast the updated foo..
    io.sockets.emit('update', foo); // how?
});

module.exports = router;

【问题讨论】:

    标签: express socket.io


    【解决方案1】:

    一种选择是将其传递给 req 对象。

    app.js:

    var express = require('express');
    var path = require('path');
    var logger = require('morgan');
    var api = require('./routes/api');
    var app = express();
    var io = require('socket.io').listen(app.listen(3000));
    
    app.use(logger('dev'));
    app.use(express.static(path.join(__dirname, 'public')));
    
    io.sockets.on('connection', function (socket) {
        console.log('client connect');
        socket.on('echo', function (data) {
            io.sockets.emit('message', data);
        });
    });
    
    // Make io accessible to our router
    app.use(function(req,res,next){
        req.io = io;
        next();
    });
    
    app.use('/api', api);
    
    // error handlers omitted
    
    module.exports = app;
    

    ./routes/api.js:

    var express = require('express');
    var router = express.Router();
    
    router.put('/foo', function(req, res) {
        /* 
          do stuff to update the foo resource 
          ...
         */
    
        // now broadcast the updated foo..
        req.io.sockets.emit('update', foo); 
    });
    
    module.exports = router;
    

    【讨论】:

    • 你真的让上面的代码工作了吗?我请求中的 io 在我的路由器代码中一直显示为未定义
    • ups 我有一个错误 - // Make io accessible to our router 部分应该在 app.use('/api', api); 之前定义
    • 为库的 v4 添加一个小的 2021 更新。要导入库,请使用import { Server as socketsIOServer } from 'socket.io';。要将套接字的服务器连接到您的快速服务器,请使用const io = new socketsIOServer(app.listen(...));。其余的工作完全相同
    【解决方案2】:

    我已经稍微修改了你的文件,你能检查一下它是否有效吗?

    您可以将您定义的 io 传递给您的路线,如下所示;

    require('./routes/api')(app,io); 
    

    我没有测试 Socket.IO 部分,但没有语法错误,路由也可以正常工作。

    server.js 文件:

    var express = require('express');
    var app = express();
    var path = require('path');
    var logger = require('morgan');
    
    var io = require('socket.io').listen(app.listen(3000));
     
    app.use(logger('dev'));
    app.use(express.static(path.join(__dirname, 'public')));
     
    io.sockets.on('connection', function (socket) {
        console.log('client connect');
        socket.on('echo', function (data) {
        io.sockets.emit('message', data);
     });
    });
    
    require('./routes/api')(app,io); 
     
    console.log("Server listening at port 3000");
    

    api.js:

    module.exports = function(app,io) {
    app.put('/foo', function(req, res) {
     
        /* 
     
          do stuff to update the foo resource 
     
          ...
     
         */
     
     
        // now broadcast the updated foo..
        
        console.log("PUT OK!");
     
        io.sockets.emit('update'); // how?
        res.json({result: "update sent over IO"});
     
    });
    }
    

    【讨论】:

    • 谢谢坎儿。这很有效,我可以通过一些小的调整来使用它,因为这个特定的应用程序相当简单。但是当我也试图用它来学习一些最佳实践时,有没有办法继续使用 express Router()?这似乎是 express 4.x 的惯用方式。
    • @darrend,根据 express.js 文件,看起来可以像这样使用 expressjs.com/api.html ,你怎么看?
    • 这很合理 - 我将标记为已接受并深入研究文档并编写更多代码。感谢您的帮助。
    • 为人们更新:我发现 Logan Tegman stackoverflow.com/questions/29872317/…987654322@ 提供了更清洁的解决方案
    • 嘿@cdagli,我试过了。但它说,Error: listen EADDRINUSE :::3000
    【解决方案3】:

    假设您想从应用程序中的任何位置访问 SocketIO,而不仅仅是在路由器中,您可以为它创建一个单例。这对我有用:

    //socket-singletion.js
    
    var socket = require('socket.io');
    
    var SocketSingleton = (function() {
      this.io = null;
      this.configure = function(server) {
        this.io = socket(server);
      }
    
      return this;
    })();
    
    module.exports = SocketSingleton;
    

    然后,您需要使用您的服务器对其进行配置:

    //server config file
    
    var SocketSingleton = require('./socket-singleton');
    var http = require('http');
    var server = http.createServer(app);
    SocketSingleton.configure(server); // <--here
    server.listen('3000');
    

    最后,随心所欲地使用它:

    //router/index.js
    
    var express = require('express');
    var router = express.Router();
    var SocketSingleton = require('../socket-singleton');
    
    /* GET home page. */
    router.get('/', function(req, res, next) {
      setTimeout(function(){
        SocketSingleton.io.emit('news', {msg: 'success!'});
      }, 3000);
      res.render('index', { title: 'Express' });
    });
    
    module.exports = router;
    

    【讨论】:

    • 在您的解决方案中,socket-singletion.js 中的这个关键字在初始化时仍未定义。
    • @abhishake 你在使用箭头函数吗?如果是这样,“this”的行为会有所不同,它可能不会像上面描述的那样工作
    【解决方案4】:

    另一种选择是使用req.app

    app.js

    const express = require('express');
    const path = require('path');
    const logger = require('morgan');
    const api = require('./routes/api');
    
    const app = express();
    const io = require('socket.io').listen(app.listen(3000));
    
    // Keep the io instance
    app.io = io;
    
    app.use(logger('dev'));
    app.use(express.static(path.join(__dirname, 'public')));
    
    // ...
    
    app.use('/api', api);
    
    module.exports = app;
    

    路由/api.js

    const express = require('express');
    const router = express.Router();
    
    router.put('/foo', function(req, res) {
        /*
         * API
         */
    
        // Broadcast the updated foo..
        req.app.io.sockets.emit('update', foo);
    });
    
    module.exports = router;
    

    【讨论】:

      【解决方案5】:

      重构Edudjr的答案。

      更改单例以创建socket.io服务器的新实例

      const { Server } = require('socket.io');
      const singleton = (() => {
          this.configure = (server) => this.io = new Server(server)
          return this
      })();
      
      module.exports = singleton
      

      初始化您的 express 应用、服务器和单例。

      // initialise app
      const app = express();
      const server = require('http').createServer(app);
      
      // configure socket.io
      socket.configure(server)
      

      然后在你的路由器中

      const socket = require('/utils/socket-singleton');
      
      socket.io.emit('event', {message: 'your message here'})
      

      【讨论】:

        【解决方案6】:

        我认为最好的方法是将 io 设置为 req 的属性,如下所示:

        app.use(function(req,res,next){
            req.io = io;
            next();
        });
        
        app.use('/your-sub-link', your-router);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-23
          • 2020-04-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-09
          相关资源
          最近更新 更多