中间件
中间件函数是在每个传入连接时执行的函数。
中间件函数可用于
- 日志记录
- 身份验证/授权
- 速率限制
注意:此函数每个连接只执行一次(即使连接包含多个 HTTP 请求)。
信息
如果您正在寻找 Express 中间件,请查看 本节.
注册中间件
中间件函数可以访问 套接字实例 和下一个注册的中间件函数。
io.use((socket, next) => {
if (isValid(socket.request)) {
next();
} else {
next(new Error("invalid"));
}
});
您可以注册多个中间件函数,它们将按顺序执行
io.use((socket, next) => {
next();
});
io.use((socket, next) => {
next(new Error("thou shall not pass"));
});
io.use((socket, next) => {
// not executed, since the previous middleware has returned an error
next();
});
请确保在任何情况下都调用 next()
。否则,连接将一直处于挂起状态,直到在给定的超时后关闭。
重要说明:套接字实例在执行中间件时实际上并未连接,这意味着如果连接最终失败,将不会发出 disconnect
事件。
例如,如果客户端手动关闭连接
// server-side
io.use((socket, next) => {
setTimeout(() => {
// next is called after the client disconnection
next();
}, 1000);
socket.on("disconnect", () => {
// not triggered
});
});
io.on("connection", (socket) => {
// not triggered
});
// client-side
const socket = io();
setTimeout(() => {
socket.disconnect();
}, 500);
发送凭据
客户端可以使用 auth
选项发送凭据
// plain object
const socket = io({
auth: {
token: "abc"
}
});
// or with a function
const socket = io({
auth: (cb) => {
cb({
token: "abc"
});
}
});
这些凭据可以在服务器端的 握手 对象中访问
io.use((socket, next) => {
const token = socket.handshake.auth.token;
// ...
});
处理中间件错误
如果 next
方法使用 Error 对象调用,则连接将被拒绝,客户端将收到 connect_error
事件。
// client-side
socket.on("connect_error", (err) => {
console.log(err.message); // prints the message associated with the error
});
您可以将其他详细信息附加到 Error 对象
// server-side
io.use((socket, next) => {
const err = new Error("not authorized");
err.data = { content: "Please retry later" }; // additional details
next(err);
});
// client-side
socket.on("connect_error", (err) => {
console.log(err instanceof Error); // true
console.log(err.message); // not authorized
console.log(err.data); // { content: "Please retry later" }
});
与 Express 中间件的兼容性
由于它们没有绑定到通常的 HTTP 请求/响应周期,因此 Socket.IO 中间件实际上与 Express 中间件 不兼容。
话虽如此,从版本 4.6.0
开始,底层引擎现在支持 Express 中间件
io.engine.use((req, res, next) => {
// do something
next();
});
中间件将针对每个传入的 HTTP 请求调用,包括升级请求。
使用 express-session
的示例
import session from "express-session";
io.engine.use(session({
secret: "keyboard cat",
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}));
使用 helmet
的示例
import helmet from "helmet";
io.engine.use(helmet());
如果中间件必须仅应用于握手请求(而不是每个 HTTP 请求),则可以检查 sid
查询参数是否存在。
使用 passport-jwt
的示例
io.engine.use((req, res, next) => {
const isHandshake = req._query.sid === undefined;
if (isHandshake) {
passport.authenticate("jwt", { session: false })(req, res, next);
} else {
next();
}
});