跳至主要內容
版本:4.x

中間件

中間件函式是針對每個傳入連線執行的函式。

中間件函式對於下列情況很有用

  • 記錄
  • 驗證 / 授權
  • 速率限制

注意:此函式只會針對每個連線執行一次(即使連線包含多個 HTTP 要求)。

資訊

如果您正在尋找 Express 中間件,請查看此區段

註冊中間件

中間件函式可以存取Socket 實例和下一個已註冊的中間件函式。

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()。否則,連線將會掛起直到在給定的逾時時間後關閉。

重要事項:當中間件執行時,Socket 實例並未實際連線,這表示如果連線最終失敗,將不會發出 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"
});
}
});

這些認證資訊可以在伺服器端的handshake 物件中存取

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 中間件相容

由於 Socket.IO 中間件並未繫結至一般的 HTTP 要求/回應循環,因此它們與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();
}
});