Express和WebSocket位于同一端口



我有一个聊天应用程序,它使用nodejs服务器,该服务器是同一端口上的express node rest服务器和web socket服务器的组合

var express = require("express"),
app     = express(),
http    = require("http"),
bodyParser  = require("body-parser"),
methodOverride = require("method-override"),
//  server  = http.createServer(app),
mongoose    = require("mongoose");

const port = process.env.OPENSHIFT_NODEJS_PORT || 3000;
app.set('port', port);
app.set('ipaddr', process.env.OPENSHIFT_NODEJS_IP || "127.0.0.1");
app.use(bodyParser.json());
app.use(methodOverride());

//Import routes
const chatsRoutes = require('./routes/chats');

app.use('/chats/', chatsRoutes);
app.get('/', (req, res) => {
res.send("Ready!");
});

/** catch 404 and forward to error handler */
app.use('*', (req, res) => {
return res.status(404).json({
success: false,
message: 'API endpoint doesnt exist'
})
});

mongoose.connect('mongodb://localhost/thorapps_eu_pokenetwork', {useNewUrlParser: true, useUnifiedTopology: true }, () =>
console.log('Connected to Mongo DB')
);

//
// Websocket
//
const WebSocket = require('ws');
let WSServer = WebSocket.Server;
let server = require('http').createServer();
let wss = new WSServer({
server: server,
perMessageDeflate: false
})
server.on('request', app);
wss.on('connection', function connection(ws) {
var req = ws.upgradeReq;
ws.on('message', function(msg) {
ws.send(JSON.stringify({
type: 'server',
//method: req.method,
//headers: req.headers,
body: msg
}, null, 't'));
});
});
server.listen(port, function() {
console.log(`Combo server on 3000`);
});

这个设置非常有效。我遇到的问题是混合了两种服务。。。例如,当用户向聊天室发送新消息时,它会转到express服务器将其注册到数据库中,但随后我想通知该聊天室中已连接的其他用户。。。。但是我怎么能从快递服务器发送websocket消息。。。。。

我的路线代码是:

// user send message to a chat
router.patch('/add_message/:chatId', async (req, res) => {
var updatedRaid;

try {
const user = req.body.user;
const chat = await Chats.findOne({_id:req.params.chatId});
const messageObj = {
username: req.body.user.username,
data_time: new Date(), // "mmmm dS, yyyy, h:MM:ss TT"),     // "6 sep 2020 23:20",
text: req.body.message,
};
chat.messages.push(messageObj);
chat.save((err, chat_result) => {
if (err) {
console.log(err.message);
}
Chats.aggregate([
{
$match: 
{
raid : mongoose.Types.ObjectId(req.params.raidId)
}
},
{
$lookup: {
from: "raids",
localField: "raid",
foreignField: "_id",
as: "raid"
}
},
{ $unwind: "$raid" },
{
$lookup: {
from: "users",
localField: "raid.users.username",
foreignField: "username",
as: "users"
}
},
{
$addFields: {
users: {
$map: {
input: "$users",
as: "t",
in: {
$mergeObjects: [
"$$t",
{
$first: {
$filter: {
input: "$raid.users",
cond: {
$eq: [
"$$this.username",
"$$t.username"
] 
}
}
}
}
]
}
}
}
}
},
{
$addFields: {
messages: {
$map: {
input: "$messages",
as: "m",
in: {
$mergeObjects: [
"$$m",
{
$first: {
$filter: {
input: "$users",
cond: {
$eq: [
"$$this.username",
"$$m.username"
] 
}
}
}
}
]
}
}
}
}
} 
]).exec((err, chat_resultant) => {
if (err) throw err;

res.json({
chatroom: chat_resultant[0],
});
})

});

} catch (err) {
res.json({message: err.message});
}
});

谢谢你的帮助。。。

我建议您将除身份验证之外的所有通信切换到WebSocket。尽管您可以将wss传递给路由,但在WebSocket中管理所有通信会更容易、更快速。REST API意味着,每一个请求都经过身份验证,当你双向使用websocket时,应用程序将承担很大责任。

我使用了几种Websocket路由来代替REST API路由。

然后:

...
wss.on('connection', (ws, req) => {     
ws.on('message', (m) => serverSubscriber(wss, ws, JSON.parse(m), req.user))
});    

const serverSubscriber = (wss, ws, m, req.user) => {
switch (m.route) {
case 'newMessage':
wss.clients.forEach((client) => {
if (c.readyState === 1) {
client.send({
route: 'messageResponse',
data: 'hello'
})
}             ​
​... 

您是否能够向一个或所有连接的客户端发送和接收消息。

要进行身份验证,请尝试ws文档中的wss.handleUpgrade:https://www.npmjs.com/package/ws#client-身份验证在这里,你可以将你的用户信息传递给WebSocket请求,这样你的订阅者就可以使用它了

编辑:评论后,另一个解决方案建议:对于这种情况,我使用内部WebSocket:只需在您的快速路由中创建新的WebSocket客户端,该客户端将连接到本地主机上的当前WebSocket。

const internalSocketClient = new WebSocket(`ws://localhost:1200/websocket/`);
router.get('/newmessage', (req, res) => {
... parse and do what you want ...
internalSocketClient.send({route: 'internalMessage', data: req.body.message})
})

和你的websocket服务器上的消息:

ws.on('message', (msg) => {
if (JSON.parse(msg).route === 'internalMessage') {
console.log('Message received from express path')
// now emit the message to all clients
}
})

但我仍然强烈建议重写代码——这并不难——我在过去几天里已经这样做了,从REST API切换到WS,优势是巨大的。

最新更新