簡體   English   中英

在 Express.js 路由中發送 websocket 消息

[英]send a websocket message in a route, Express.js

目標:

假設我們有一個充滿商店的市場。 我正在為店主創建一個特定頁面localhost:3000/livePurchases/:storeId來監控他們何時獲得新購買的實時通知。

在此處輸入圖像描述

在此處輸入圖像描述

alert('you received a new purchase')應在購買商品時由 WebSocket 觸發。

問題:

我需要在我的 express 服務器上設置 WebSocket,以便可以在我的代碼中與設置 websocket 服務器的位置不同的地方觸發 websocket。 但我不明白如何做到這一點。

在成功購買商品后,客戶的瀏覽器將請求路由/newPurchase/:storeId 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;
};

但是 app.js 是從另一個腳本www.js導出和運行的。 在實際場景中,這是在運行應用程序之前連接數據庫:

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;

這意味着需要在www.js中設置 web 套接字服務器。

下面是我從本教程中獲得的通知服務,它似乎試圖解決我遇到的問題,但沒有解釋如何實現它。 它是一個處理 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;

我停止實施“NotifierService”的地方

我在 www.js 中添加了帶有NotifierServicewww.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;

但是現在如何從后端app.js中的/newPurchase路由發送 websocket 消息? 如果我在app.js中創建一個新的NotifierService實例,以便在/newPurchase路由中使用notifierService.send方法,那么新的NotifierService實例將無法訪問 websocket 連接,因為它與一項在www.js上發起。

前端:

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

我能夠將 websocket 實例移動到app.js而不是www.js 然后我只是將該實例傳遞給其他路線。

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以表明您可以移動通知程序實例並從任何路由調用它。

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

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM