[英]send a websocket message in a route, Express.js
Let's say we have a marketplace full of shops.假设我们有一个充满商店的市场。 I'm creating a specific page
localhost:3000/livePurchases/:storeId
for a shop owner to monitor live notifications of when they got a new purchase.我正在为店主创建一个特定页面
localhost:3000/livePurchases/:storeId
来监控他们何时获得新购买的实时通知。
alert('you received a new purchase')
should be triggered by a WebSocket when an item is purchased. alert('you received a new purchase')
应在购买商品时由 WebSocket 触发。
I need to set up WebSockets on my express server so that the websocket can be triggered somewhere in my code different from where the websocket server was set up.我需要在我的 express 服务器上设置 WebSocket,以便可以在我的代码中与设置 websocket 服务器的位置不同的地方触发 websocket。 But I don't understand how to do this.
但我不明白如何做到这一点。
The route /newPurchase/:storeId
would be requested by the browser of a customer after they successfully purchase an item.在成功购买商品后,客户的浏览器将请求路由
/newPurchase/:storeId
。 The websocket should send a message within the code of the route "/newPurchase/:storeId"
(backend) to the websocket on "/livePurchases/:storeId"
(frontend) where the shop owner can monitor live purchases. websocket 应在
"/newPurchase/:storeId"
(后端)的路由代码内向"/livePurchases/:storeId"
(前端)上的 websocket 发送一条消息,店主可以在其中监控实时购买。
app.js
const express = require("express");
module.exports = (config) => {
const app = express();
app.post("/newPurchase/:storeId", (req, res, next) => {
const { storeId } = req.params;
// trigger websocket message to `localhost:3000/livePurchases/:storeId`
// when client requests this route
});
return app;
};
But app.js is exported and run from another script, www.js
.但是 app.js 是从另一个脚本
www.js
导出和运行的。 In real scenarios this is to connect a database before running the app.:在实际场景中,这是在运行应用程序之前连接数据库:
www.js
const app = require("../server/app")();
const port = process.env.PORT || "4000";
app.set("port", port);
app
.listen(port)
.on("listening", () =>
console.log("info", `HTTP server listening on port ${port}`)
);
module.exports = app;
So that means that the web socket server needs to be set up in www.js
.这意味着需要在
www.js
中设置 web 套接字服务器。
Below is a notifier service I got it from this tutorial , which seemed like it was trying to solve the problem I have, but it didn't explain how to implement it.下面是我从本教程中获得的通知服务,它似乎试图解决我遇到的问题,但没有解释如何实现它。 It is a class that handles the websocket.
它是一个处理 websocket 的 class。
NotifierService.js
const url = require("url");
const { Server } = require("ws");
class NotifierService {
constructor() {
this.connections = new Map();
}
connect(server) {
this.server = new Server({ noServer: true });
this.interval = setInterval(this.checkAll.bind(this), 10000);
this.server.on("close", this.close.bind(this));
this.server.on("connection", this.add.bind(this));
server.on("upgrade", (request, socket, head) => {
console.log("ws upgrade");
const id = url.parse(request.url, true).query.storeId;
if (id) {
this.server.handleUpgrade(request, socket, head, (ws) =>
this.server.emit("connection", id, ws)
);
} else {
socket.destroy();
}
});
}
add(id, socket) {
console.log("ws add");
socket.isAlive = true;
socket.on("pong", () => (socket.isAlive = true));
socket.on("close", this.remove.bind(this, id));
this.connections.set(id, socket);
}
send(id, message) {
console.log("ws sending message");
const connection = this.connections.get(id);
connection.send(JSON.stringify(message));
}
broadcast(message) {
console.log("ws broadcast");
this.connections.forEach((connection) =>
connection.send(JSON.stringify(message))
);
}
isAlive(id) {
return !!this.connections.get(id);
}
checkAll() {
this.connections.forEach((connection) => {
if (!connection.isAlive) {
return connection.terminate();
}
connection.isAlive = false;
connection.ping("");
});
}
remove(id) {
this.connections.delete(id);
}
close() {
clearInterval(this.interval);
}
}
module.exports = NotifierService;
I added the websocket server with the NotifierService
in www.js
我在 www.js 中添加了带有
NotifierService
的www.js
服务器
www.js with websockets added
const app = require("../server/app")();
const NotifierService = require("../server/NotifierService.js");
const notifier = new NotifierService();
const http = require("http");
const server = http.createServer(app);
notifier.connect(server);
const port = process.env.PORT || "4000";
app.set("port", port);
server
.listen(port)
.on("listening", () =>
console.log("info", `HTTP server listening on port ${port}`)
);
module.exports = app;
But now how do I send the websocket message from the /newPurchase
route in app.js
on the backend?但是现在如何从后端
app.js
中的/newPurchase
路由发送 websocket 消息? If I create a new instance of NotifierService
in app.js
in order to use the notifierService.send
method in the /newPurchase
route, then the new NotifierService
instance won't have access to the websocket connections because it would be a different instance than the one initiated on www.js
.如果我在
app.js
中创建一个新的NotifierService
实例,以便在/newPurchase
路由中使用notifierService.send
方法,那么新的NotifierService
实例将无法访问 websocket 连接,因为它与一项在www.js
上发起。
Front End:前端:
App.js
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import LiveStorePurchases from "./LiveStorePurchases";
function App(props) {
return (
<div className="App">
<Router>
<Switch>
<Route exact path="/livePurchases/:storeId">
<LiveStorePurchases />
</Route>
</Switch>
</Router>
</div>
);
}
export default App;
LivePurchaseServer.js
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
export default function LiveStorePurchases() {
let { storeId } = useParams();
const URL = "ws://127.0.0.1:4000?storeId=" + storeId;
const [ws, setWs] = useState(new WebSocket(URL));
useEffect(() => {
ws.onopen = (e) => {
newFunction(e);
function newFunction(e) {
alert("WebSocket Connected");
}
};
ws.onmessage = (e) => {
const message = e.data;
alert(message);
};
return () => {
ws.onclose = () => {
alert("WebSocket Disconnected");
setWs(new WebSocket(URL));
};
};
}, [ws.onmessage, ws.onopen, ws.onclose, ws, URL]);
return (
<div
style={{
color: "red",
fontSize: "4rem",
}}
>
store: {storeId}
</div>
);
}
app.js
: app.js
:
I was able to move the websocket instance to app.js
instead of www.js
.我能够将 websocket 实例移动到
app.js
而不是www.js
。 Then I simply passed that instance around to other routes.然后我只是将该实例传递给其他路线。
const express = require("express");
const NotifierService = require("../server/NotifierService.js");
const notifier = new NotifierService();
const http = require("http");
const routes = require("./routes");
module.exports = (config) => {
const app = express();
const server = http.createServer(app); // websocket created
notifier.connect(server); // and connected here in app.js
// I moved POST /newPurchase to routes.js in order
// to demonstrate how the notifier instance can be
// passed around to different routes
app.use(routes(notifier));
return server;
};
routes.js
: routes.js
:
I created a routes file routes.js
to show that you could move the notifier instance around and call it from any route.我创建了一个路由文件
routes.js
以表明您可以移动通知程序实例并从任何路由调用它。
const express = require("express");
const router = express.Router();
// I moved "/newPurchase/:id" to this routes.js file to show that
// I could move the notifier instance around.
module.exports = (webSocketNotifier) => {
router.post("/newPurchase/:id", (req, res, next) => {
webSocketNotifier.send(req.params.id, "purchase made");
res.status(200).send();
});
return router;
};
www.js
: www.js
:
const server = require("../server/app")();
const port = process.env.PORT || "4000";
server
.listen(port)
.on("listening", () =>
console.log("info", `HTTP server listening on port ${port}`)
);
module.exports = server;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.