express.js 4 and sockets with express router

2019-01-17 12:00发布

I'm trying to create a really simple node API using express.js 4 but I need a few 'realtime' events for which I added socket.io. I'm fairly new to both so I'm likely missing something basic but I can't find good docs/tuts on this.

In the express app (created with the express generator) I have something like this based on simple examples and project docs that I read. This works OK and from client apps, I can send/receive the socket events:

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;

but I want to use the sockets from my API routes (in the ./routes/api.js file that I 'require' above). For example, someone might use the API to PUT/POST a resource and I want that broadcast to connected socket.io clients.

I cannot see how to use the 'io' variable or organise the code currently in the io.sockets.on('connection' ... function inside express routes. Here's the ./routes/api.js file:

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;

4条回答
Bombasti
2楼-- · 2019-01-17 12:43

One more option is to use 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;

routes/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;
查看更多
做个烂人
3楼-- · 2019-01-17 12:48

One option is to pass it in to req object.

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;
查看更多
小情绪 Triste *
4楼-- · 2019-01-17 12:53

I've modified your files a little bit, may you check if it works?

You can pass the io you've defined to your routes like below;

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

I didn't test the Socket.IO parts but there is no syntax error and routes also working.

server.js file:

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"});

});
}
查看更多
三岁会撩人
5楼-- · 2019-01-17 12:58

Supposing you want to access the SocketIO from anywhere in your application, not just in the router, you could create a singleton for it. This is what works for me:

//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

Then, you need to configure it using your server:

//server config file

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

Finally, use it wherever you want:

//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;
查看更多
登录 后发表回答