【问题标题】:Use socket.io in expressjs routes instead of in main server.js file在 expressjs 路由中使用 socket.io 而不是在主 server.js 文件中
【发布时间】:2018-05-29 23:30:01
【问题描述】:

我有 express/nodejs api。我正在向它添加 socket.io 功能。目前我的所有路由都在单独的文件夹中,我将它们包含在 server.js 文件中并将它们用作 app.use() 函数。

在 server.js 文件中,我还通过监听特定端口(例如 3000)来启动 express 服务器,如下所示。

let server = app.listen(3000);

根据所有谷歌搜索,我发现我需要传递服务器变量来初始化 socket.io,如下所示。

let io = require('socket.io')(server);

现在的问题是,既然它需要这个变量,那么我如何在不同文件夹中的路由文件中使用 socket.io 来发送和接收来自客户端的事件?

更新

在 server.js 文件中

let route = require('./routes/route');

let app = express();

let server = app.listen(3000);

console.log('Listening to port');

let io = require('socket.io')(server);

app.use('/api/1.0/events', route(io));

在 route.js 文件中

let express = require('express');

module.exports = (io) => {
    console.log('IO: ', io);
};

更新 2

server.js 文件

let express = require('express');
let events = require('./routes/events');
let app = express();
let server = app.listen(3000);

let io = require('socket.io')(server);


app.use(function(request, response, next) {
    request.io = io;
    next();
});

app.use('/events', events);

events.js 文件

let express = require('express');

let Events = require('../models/events');

apiRoutes.post('/new', function(request, response) {
    let newEvent = new Events(request.body);

    newEvent.save((error, result) => {
        if (error) {
            response.json(error);
        } else {
            // console.log('ELSE');
            // request.io.on('connect', socket => {
                // console.log('LISTENING TO SOCKET...');

                request.io.on('EventCreated', data => {
                    console.log('DATA ON Server: ', data);
                });
            // });

            response.json({
                success: true,
                message: 'New event created'
            });
        }
    });
});

【问题讨论】:

  • 使用 module.exports 将您的 io 变量导出到其他文件
  • 感谢您的回复,但如何导出?我的意思是在哪个文件中。我在 server.js 文件中需要它。你能提供任何示例代码吗?
  • 你永远不想在像request.io.on()这样的请求处理程序中安装一个事件处理程序,因为每次路由被命中时你都会添加一个新的事件处理程序,它们会永远堆积起来。另外,您不会使用io.on() 监听单个套接字事件。您使用socket.on() 侦听消息事件。所以,你在这里的整个方法是错误的。您必须备份并从一开始就描述您试图解决的整体问题,以便我们帮助您解决这个问题。但是,由于问题的不同部分已经有好几天了,我认为您需要为此提出一个新问题。
  • 我要解决的问题是用户将创建一个事件。用户将填写表单并按下创建事件按钮(单击“创建事件”按钮并关闭表单,用户会看到事件列表页面)。它将数据发送到服务器并存储数据库。一旦它被存储,然后发出新事件并在客户端上监听该事件。并使用新创建的事件更新事件页面上的事件列表。

标签: javascript node.js sockets express


【解决方案1】:

在 server.js 中:

module.exports.getIO = function(){
     return io;
}

在您的路线文件中:

var theServer = require('<path to server.js>');
var iovar = theServer.getIO(); //your io var

【讨论】:

    【解决方案2】:

    有多种方法可以将io 变量与路由文件共享。

    1. 当您在路由文件中require() 时,将io 变量作为构造函数参数传递给它。

    2. 使用app.set("io", io),这样您就可以在任何有权访问app 对象的文件中使用let io = app.get("io")

    3. 创建一个中间件,将 io 对象放在每个 req 对象上,以便您可以随时从那里访问它。


    这是一个将其作为构造函数参数传递给路由器文件的示例:

    let server = app.listen(3000);
    let io = require('socket.io')(server);
    
    // load other routers
    app.use(require("./someRouterFile.js")(io));
    
    // in someRouterFile.js
    const express = require('express');
    
    module.exports = function(io) {
        let router = express.Router()
    
        // define routes
        // io is available in this scope
        router.get(...)
    
        return router;
    }
    

    这是app.set() 方案的示例:

    let server = app.listen(3000);
    let io = require('socket.io')(server);
    app.set("io", io);
    

    然后,在您可以访问app 对象的路径中的任何位置,您都可以通过以下方式获取它:

    let io = app.get("io");
    

    这是一个使用中间件将 io 对象设置到每个 req 对象上的示例,以便所有路由都可以使用它。

    let server = app.listen(3000);
    let io = require('socket.io')(server);
    
    // place this middleware before any other route definitions
    // makes io available as req.io in all request handlers
    app.use(function(req, res, next) {
        req.io = io;
        next();
    });
    
    // then in any express route handler, you can use req.io.emit(...)
    

    这是一个在没有中间件的情况下使用模块构造函数参数的示例:

    // in mysocket.js
    module.exports = (io) => {
        console.log('IO: ', io);
        io.on('connect', socket => {
           // handle various socket connections here
        });
    
        // put any other code that wants to use the io variable
        // in here
    
    
    };
    

    然后,在你的主文件中:

    let server = app.listen(3000);
    let io = require('socket.io')(server);
    
    // initialize my socketio module and pass it the io instance
    require('./mysocket.js')(io);
    

    【讨论】:

    • 谢谢,我已经尝试过 app.set() 和中间件方法,但不知何故它们对我不起作用。第一个听起来很有趣,但不知何故我收到了这个错误/node_modules/express/lib/router/index.js:458 throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn)) ^ TypeError: Router.use() requires a middleware function but got a undefined
    • @null - 我必须查看您尝试知道如何帮助您修复它的实际代码。
    • @null - 你对中间件的尝试只是一个错误的实现。请查看我的答案中的中间件或路由器实现,并从中复制其中一个。您正在调用 route(io) 并将返回值从该中间件传递给中间件,为此,它必须返回另一个需要中间件参数的函数,但您的 route(io) 函数不返回任何内容,因此您会得到错误.
    • 如您所见,它实际上并不是一个中间件。我正在尝试实施您的第一种方法。
    • @b4rtekb - 在服务器端,connectconnection 事件是 synonyms(同样的事情)。
    【解决方案3】:

    分享我用过的解决方案

    这个问题的另一个解决方案(我实际使用过的)是我们可以使我们的iosocket.io object)对象成为全局对象。

    只需将io 分配给global.io

    io 对象在index.js 中,在创建和设置io 对象之后只需执行global(即global.io = io;),现在这个io 对象可以在我们所在的任何路径中访问在我们的项目中工作。

    【讨论】:

    • 我正在将项目从使用单个 main.js 文件重构为分离的路由和中间件文件。以前我在main.js 中有var logged_in_users = {};var users_in_rooms = {}; 来跟踪套接字ID 等,并且在同一文件中定义的中间件可以访问和更新这些变量。现在我已经转移到单独的中间件和路由文件,似乎中间件文件无权访问这些全局变量?我从中间件文件中得到错误:ReferenceError: logged_in_users is not defined。您对如何解决此问题有任何建议或知道任何资源吗?
    猜你喜欢
    • 1970-01-01
    • 2019-10-01
    • 2013-09-22
    • 2016-10-18
    • 2016-03-14
    • 2017-07-27
    • 2021-09-11
    • 1970-01-01
    • 2012-09-16
    相关资源
    最近更新 更多