中间件

中间件函数是为每个传入连接执行的函数。

中间件函数可用于:

  • logging
  • authentication / authorization
  • rate limiting

Note: this function will be executed only once per connection (even if the connection consists in multiple HTTP requests).

注册中间件

中间件函数可以访问Socket 实例和下一个注册的中间件函数。

  1. io.use((socket, next) => {
  2. if (isValid(socket.request)) {
  3. next();
  4. } else {
  5. next(new Error("invalid"));
  6. }
  7. });

您可以注册几个中间件函数,它们将按顺序执行:

  1. io.use((socket, next) => {
  2. next();
  3. });
  4. io.use((socket, next) => {
  5. next(new Error("thou shall not pass"));
  6. });
  7. io.use((socket, next) => {
  8. // not executed, since the previous middleware has returned an error
  9. next();
  10. });

请确保在任何情况下都调用next()。 否则,连接将一直挂起,直到在给定超时后关闭。

重要提示:执行中间件时,Socket 实例实际上并未连接,这意味着disconnect如果连接最终失败,则不会发出任何事件。

例如,如果客户端手动关闭连接:

  1. // server-side
  2. io.use((socket, next) => {
  3. setTimeout(() => {
  4. // next is called after the client disconnection
  5. next();
  6. }, 1000);
  7. socket.on("disconnect", () => {
  8. // not triggered
  9. });
  10. });
  11. io.on("connection", (socket) => {
  12. // not triggered
  13. });
  14. // client-side
  15. const socket = io();
  16. setTimeout(() => {
  17. socket.disconnect();
  18. }, 500);

发送凭据

auth客户端可以使用以下选项发送凭据:

  1. // plain object
  2. const socket = io({
  3. auth: {
  4. token: "abc"
  5. }
  6. });
  7. // or with a function
  8. const socket = io({
  9. auth: (cb) => {
  10. cb({
  11. token: "abc"
  12. });
  13. }
  14. });

可以在服务器端的握手对象中访问这些凭据:

  1. io.use((socket, next) => {
  2. const token = socket.handshake.auth.token;
  3. // ...
  4. });

处理中间件错误

如果next使用 Error 对象调用该方法,则连接将被拒绝并且客户端将收到一个connect_error事件。

  1. // client-side
  2. socket.on("connect_error", (err) => {
  3. console.log(err.message); // prints the message associated with the error
  4. });

您可以将其他详细信息附加到错误对象:

  1. // server-side
  2. io.use((socket, next) => {
  3. const err = new Error("not authorized");
  4. err.data = { content: "Please retry later" }; // additional details
  5. next(err);
  6. });
  7. // client-side
  8. socket.on("connect_error", (err) => {
  9. console.log(err instanceof Error); // true
  10. console.log(err.message); // not authorized
  11. console.log(err.data); // { content: "Please retry later" }
  12. });

与Express中间件的兼容性

大多数现有的Express 中间件模块应该与 Socket.IO 兼容,您只需要一个小包装函数来使方法签名匹配:

  1. const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);

结束请求-响应周期并且不调用的中间件函数next()将不起作用。

express-session示例:

  1. const session = require("express-session");
  2. io.use(wrap(session({ secret: "cats" })));
  3. io.on("connection", (socket) => {
  4. const session = socket.request.session;
  5. });

Passport示例:

  1. const session = require("express-session");
  2. const passport = require("passport");
  3. io.use(wrap(session({ secret: "cats" })));
  4. io.use(wrap(passport.initialize()));
  5. io.use(wrap(passport.session()));
  6. io.use((socket, next) => {
  7. if (socket.request.user) {
  8. next();
  9. } else {
  10. next(new Error("unauthorized"))
  11. }
  12. });

可以在此处找到 Passport 的完整示例。