Использовать socket.io внутри файла экспресс-маршрутов
Я пытаюсь использовать Socket.io с Node.js и испускать сокет в логике маршрута.
У меня довольно стандартная версия Express 3 с файлом server.js, который находится на маршруте, а затем у меня есть index.js, который находится в папках маршрутов, которые экспортируют все страницы/общедоступные функции сайта. Поэтому они выглядят так:
exports.index = function (req, res) {
res.render('index', {
title: "Awesome page"
});
};
с маршрутизацией, определенной в server.js, например:
app.get('/',routes.index);
Я предполагаю, что мне нужно создать объект socket.io в server.js, так как ему нужен объект сервера, но как я могу получить доступ к этому объекту и испускать его из функций экспорта index.js?
Ответы
Ответ 1
Вы можете настроить файл маршрутов как функцию и передать объект Socket.IO при необходимости файла.
module.exports = function(io) {
var routes = {};
routes.index = function (req, res) {
io.sockets.emit('payload');
res.render('index', {
title: "Awesome page"
});
};
return routes;
};
Затем требуются такие маршруты:
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var routes = require('./routes')(io);
Ответ 2
Теперь есть лучший способ сделать это с помощью Express 4.0.
Вы можете использовать app.set() для хранения ссылки на объект io
.
Базовая конфигурация:
var app = require('express')();
var server = app.listen(process.env.PORT || 3000);
var io = require('socket.io')(server);
// next line is the money
app.set('socketio', io);
Внутренний маршрут или промежуточное ПО:
exports.foo = function(req,res){
// now use socket.io in your routes file
var io = req.app.get('socketio');
io.emit('hi!');
}
Информация о app.set()
и app.get()
приведена ниже:
app.set(name, value)
Назначает имя настройки для значения. Вы можете хранить любое значение, которое хотите, но определенные имена могут быть использованы для настройки поведения сервера. Эти специальные имена перечислены в таблице настроек приложения.
Вызов app.set('foo', true)
для логического свойства аналогичен вызову звонит app.enable('foo')
. Точно так же, вызывая app.set('foo', false)
для логического свойства аналогично вызову app.disable('foo')
.
Получить значение настройки с помощью app.get()
.
Источник: https://expressjs.com/en/api.html#app.set
Ответ 3
Ответ aarosil был отличным, но я столкнулся с той же проблемой, что и Виктор, с управлением клиентскими подключениями, используя этот подход. Для каждой перезагрузки на клиенте вы получите на сервере столько же повторяющихся сообщений (2 повторных = 2 дубликата, 3 = 3 дубликата и т.д.).
Развернувшись на ответе aarosil, я использовал этот подход для использования объекта сокета в файле маршрутов и управления сообщениями/управления дублирующимися сообщениями:
Внутренний файл сервера
// same as aarosil (LIFESAVER)
const app = require('express')();
const server = app.listen(process.env.PORT || 3000);
const io = require('socket.io')(server);
// next line is the money
app.set('socketio', io);
Внутри файла маршрутов
exports.foo = (req,res) => {
let socket_id = [];
const io = req.app.get('socketio');
io.on('connection', socket => {
socket_id.push(socket.id);
if (socket_id[0] === socket.id) {
// remove the connection listener for any subsequent
// connections with the same ID
io.removeAllListeners('connection');
}
socket.on('hello message', msg => {
console.log('just got: ', msg);
socket.emit('chat message', 'hi from server');
})
});
}
Ответ 4
Неправильно использовать
global.io = require('socket.io').listen(server);
Ответ 5
Слишком позднее добавление здесь, но я хотел получить доступ к сокету в моих маршрутах и специально хотел передать сообщение после сохранения в базе данных. Я использовал ответ, предоставленный @aarosil, для установки/получения объекта io, отправил каждому клиенту свой идентификатор сокета при подключении, затем использовал идентификатор сокета в маршруте, чтобы иметь возможность использовать socket.broadcast.emit()
вместо io.emit()
.
На сервере:
const io = require('socket.io')(server)
app.set('socketio', io)
io.on('connect', socket => {
socket.emit('id', socket.id) // send each client their socket id
})
Я отправляю идентификатор сокета с каждым требованием, а затем могу выполнить следующие действия в своих маршрутах:
router.post('/messages', requireToken, (req, res, next) => {
// grab the id from the request
const socketId = req.body.message.socketId
// get the io object ref
const io = req.app.get('socketio')
// create a ref to the client socket
const senderSocket = io.sockets.connected[socketId]
Message.create(req.body.message)
.then(message => {
// in case the client was disconnected after the request was sent
// and there no longer a socket with that id
if (senderSocket) {
// use broadcast.emit to message everyone except the original
// sender of the request !!!
senderSocket.broadcast.emit('message broadcast', { message })
}
res.status(201).json({ message: message.toObject() })
})
.catch(next)
})
Ответ 6
module.parent.exports.server
также будет работать, если вы экспортируете сервер в родительский модуль.