ミドルウェア
ミドルウェア関数は、すべての受信接続に対して実行される関数です。
ミドルウェア関数は以下の用途に役立ちます
- ロギング
- 認証/認可
- レート制限
注: この関数は、接続ごとに一度だけ実行されます (接続が複数の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ミドルウェアとの互換性
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();
}
});