如何与 express-session
一起使用
¥How to use with express-session
让我们从一个基本的应用开始:
¥Let's start from a basic application:
- CommonJS
- ES modules
- TypeScript
const express = require("express");
const { createServer } = require("node:http");
const { join } = require("node:path");
const { Server } = require("socket.io");
const session = require("express-session");
const port = process.env.PORT || 3000;
const app = express();
const httpServer = createServer(app);
const sessionMiddleware = session({
secret: "changeit",
resave: true,
saveUninitialized: true,
});
app.use(sessionMiddleware);
app.get("/", (req, res) => {
res.sendFile(join(__dirname, "index.html"));
});
app.post("/incr", (req, res) => {
const session = req.session;
session.count = (session.count || 0) + 1;
res.status(200).end("" + session.count);
});
const io = new Server(httpServer);
httpServer.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});
import express from "express";
import { createServer } from "node:http";
import { Server } from "socket.io";
import session from "express-session";
const port = process.env.PORT || 3000;
const app = express();
const httpServer = createServer(app);
const sessionMiddleware = session({
secret: "changeit",
resave: true,
saveUninitialized: true,
});
app.use(sessionMiddleware);
app.get("/", (req, res) => {
res.sendFile(new URL("./index.html", import.meta.url).pathname);
});
app.post("/incr", (req, res) => {
const session = req.session;
session.count = (session.count || 0) + 1;
res.status(200).end("" + session.count);
});
const io = new Server(httpServer);
httpServer.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});
import express = require("express");
import { createServer } from "node:http";
import { Server } from "socket.io";
import session from "express-session";
declare module "express-session" {
interface SessionData {
count: number;
}
}
const port = process.env.PORT || 3000;
const app = express();
const httpServer = createServer(app);
const sessionMiddleware = session({
secret: "changeit",
resave: true,
saveUninitialized: true,
});
app.use(sessionMiddleware);
app.get("/", (req, res) => {
res.sendFile(new URL("./index.html", import.meta.url).pathname);
});
app.post("/incr", (req, res) => {
const session = req.session;
session.count = (session.count || 0) + 1;
res.status(200).end("" + session.count);
});
const io = new Server(httpServer);
httpServer.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});
你将需要这些附加类型:
¥You'll need those additional types:
npm install @types/express @types/express-session
共享会话上下文
¥Sharing the session context
会话上下文可以通过调用与 Socket.IO 服务器共享:
¥The session context can be shared with the Socket.IO server by calling:
io.engine.use(sessionMiddleware);
就如此容易!你现在可以访问 session
对象:
¥As simple as that! You'll now have access to the session
object:
io.on("connection", (socket) => {
const session = socket.request.session;
});
使用会话 ID
¥Using the session ID
你可以使用会话 ID 在 Express 和 Socket.IO 之间建立链接:
¥You can use the session ID to make the link between Express and Socket.IO:
io.on("connection", (socket) => {
const sessionId = socket.request.session.id;
// the session ID is used as a room
socket.join(sessionId);
});
然后,你可以通知 /incr
处理程序中的每个连接的客户端:
¥You can then notify every connected client in the /incr
handler:
app.post("/incr", (req, res) => {
const session = req.session;
session.count = (session.count || 0) + 1;
res.status(200).end("" + session.count);
io.to(session.id).emit("current count", session.count);
});
注销流程相同:
¥Same for the log-out flow:
app.post("/logout", (req, res) => {
const sessionId = req.session.id;
req.session.destroy(() => {
// disconnect all Socket.IO connections linked to this session ID
io.in(sessionId).disconnectSockets();
res.status(204).end();
});
});
修改会话
¥Modifying the session
由于它不绑定到单个 HTTP 请求,因此必须手动重新加载并保存会话:
¥Since it is not bound to a single HTTP request, the session must be manually reloaded and saved:
- CommonJS
- ES modules
- TypeScript
io.on("connection", (socket) => {
const req = socket.request;
socket.on("my event", () => {
req.session.reload((err) => {
if (err) {
return socket.disconnect();
}
req.session.count++;
req.session.save();
});
});
});
io.on("connection", (socket) => {
const req = socket.request;
socket.on("my event", () => {
req.session.reload((err) => {
if (err) {
return socket.disconnect();
}
req.session.count++;
req.session.save();
});
});
});
import { type Request } from "express";
io.on("connection", (socket) => {
const req = socket.request as Request;
socket.on("my event", () => {
req.session.reload((err) => {
if (err) {
return socket.disconnect();
}
req.session.count++;
req.session.save();
});
});
});
你还可以使用 中间件,它将为每个传入数据包触发:
¥You can also use a middleware which will be triggered for each incoming packet:
io.on("connection", (socket) => {
const req = socket.request;
socket.use((__, next) => {
req.session.reload((err) => {
if (err) {
socket.disconnect();
} else {
next();
}
});
});
// and then simply
socket.on("my event", () => {
req.session.count++;
req.session.save();
});
});
调用 req.session.reload()
更新 req.session
对象:
¥Calling req.session.reload()
updates the req.session
object:
io.on("connection", (socket) => {
const session = socket.request.session;
socket.use((__, next) => {
session.reload(() => {
// WARNING! "session" still points towards the previous session object
});
});
});
处理会话过期
¥Handling session expiration
你可能还需要定期重新加载会话,以防会话过期(例如,如果客户端在较长时间内未发送任何事件):
¥You may also want to periodically reload the session, in case it expires (for example if the client does not send any event for an extended period of time):
const SESSION_RELOAD_INTERVAL = 30 * 1000;
io.on("connection", (socket) => {
const timer = setInterval(() => {
socket.request.session.reload((err) => {
if (err) {
// forces the client to reconnect
socket.conn.close();
// you can also use socket.disconnect(), but in that case the client
// will not try to reconnect
}
});
}, SESSION_RELOAD_INTERVAL);
socket.on("disconnect", () => {
clearInterval(timer);
});
});
跨站请求注意事项
¥Notes for cross-site requests
express-session
依赖 cookie 将会话保留在浏览器中。因此,如果你的前端域与后端域不同(例如,如果你的计算机上运行着 SPA,但端口不同),那么你将需要发送适当的 CORS 标头:
¥express-session
relies on a cookie to persist the session in the browser. So if your frontend domain is different from your backend domain (for example, if you have a SPA running on your machine but on a different port), then you will need to send the appropriate CORS headers:
- CommonJS
- ES modules
- TypeScript
const cors = require("cors");
const corsOptions = {
origin: ["http://localhost:4200"],
credentials: true
};
// for Express
app.use(cors(corsOptions));
// for Socket.IO
const io = new Server(httpServer, {
cors: corsOptions
});
import cors from "cors";
const corsOptions = {
origin: ["http://localhost:4200"],
credentials: true
};
// for Express
app.use(cors(corsOptions));
// for Socket.IO
const io = new Server(httpServer, {
cors: corsOptions
});
import cors = require("cors");
const corsOptions = {
origin: ["http://localhost:4200"],
credentials: true
};
// for Express
app.use(cors(corsOptions));
// for Socket.IO
const io = new Server(httpServer, {
cors: corsOptions
});
你还需要在客户端将 withCredentials
选项设置为 true
:
¥You will also need to set the withCredentials
option to true
on the client side:
import { io } from "socket.io-client";
const socket = io("http://localhost:3000", {
withCredentials: true
});
与 express-session
的兼容性就这样了。谢谢阅读!
¥That's it for the compatibility with express-session
. Thanks for reading!
- CommonJS
- ES modules
- TypeScript