Skip to content

2.2 中间件设计与使用

中间件的核心思想是为应用的各个阶段和层次提供一种可插拔的、可扩展的机制。

提示

“没有任何两个 应用组件 是无法通过一个 中间件 连接的,如果不行,那就再加一个。”

1. 中间件的设计原则

  1. 单一职责

    • 每个中间件应该有明确的功能,尽量避免做多余的事情。将职责划分清晰,便于维护和扩展。
    • 示例:一个负责 认证 的中间件,另一个负责 日志记录,每个中间件做自己该做的事。
  2. 顺序性

    • 中间件的执行顺序非常重要。请求会按照中间件的注册顺序进行处理,而响应会反向执行。因此,确保中间件的顺序对功能执行至关重要。
    • 示例:认证中间件应该在路由处理之前运行,错误处理中间件应该在所有其他中间件后面处理。
  3. 可重用性

    • 中间件的功能应具有一定的通用性,可以在不同的应用、不同的路由上复用。例如,日志记录和请求数据验证等常见功能应当封装成可复用的中间件。
    • 示例:一个用于记录请求日志的中间件,可以在多个路由中复用。
  4. 可测试性

    • 中间件应易于独立测试,确保它的功能在不同场景下正常工作。通过单元测试和集成测试可以帮助发现潜在的错误。
    • 示例:可以为认证中间件编写测试,模拟用户请求并检查是否正确处理认证逻辑。
  5. 错误处理

    • 通过集中管理错误处理,避免代码重复。错误处理中间件用于捕获应用中未处理的错误,并返回合适的响应。
    • 示例:如果在一个中间件中发生错误,错误处理中间件可以捕获并返回合适的错误信息或执行一些清理工作。

2. 中间件的使用

  1. 全局中间件

    • 定义:在应用启动时注册的中间件,它们对所有的请求和路由生效。
    • 使用场景:用于跨所有路由的通用功能,如请求日志记录、跨域请求支持、全局认证等。
    • 示例:在 Express 中,使用 app.use() 来注册全局中间件:
      javascript
      app.use(express.json()); // 处理 JSON 请求体
      app.use(logger); // 全局日志中间件
  2. 路由中间件

    • 定义:在特定路由上注册的中间件,只对匹配该路由的请求生效。
    • 使用场景:用于路由特定的功能,如某些请求路径需要特定的认证、权限检查或数据处理。
    • 示例:路由中间件示例:
      javascript
      app.get("/user", authenticate, getUserData);
      上述中间件 authenticate 仅对 /user 路由有效。
  3. 错误处理中间件

    • 定义:专门用于捕获和处理错误的中间件。Express 中的错误处理中间件有四个参数(err, req, res, next)。
    • 使用场景:用于集中捕获应用中的异常错误,并返回标准化的错误响应。
    • 示例:错误处理中间件示例:
      javascript
      app.use((err, req, res, next) => {
        console.error(err.stack);
        res.status(500).json({ message: "Internal Server Error" });
      });

3. 中间件的实现

中间件函数本质上是一个接受 reqresnext 作为参数的函数。reqres 用于访问请求和响应对象,而 next 用于将控制权交给下一个中间件。

3.1 中间件函数结构

javascript
function myMiddleware(req, res, next) {
  // 执行一些操作,比如日志记录、验证、数据预处理等
  console.log("Request received");

  // 调用 next() 将控制权交给下一个中间件
  next();
}

3.2 中间件示例

  1. 日志中间件:记录每个请求的相关信息。

    javascript
    function logMiddleware(req, res, next) {
      console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
      next();
    }
  2. 认证中间件:检查请求中的认证信息。

    javascript
    function authenticateMiddleware(req, res, next) {
      if (!req.headers["authorization"]) {
        return res.status(403).json({ message: "Unauthorized" });
      }
      next();
    }
  3. 错误处理中间件:捕获错误并返回标准化的错误响应。

    javascript
    function errorHandlingMiddleware(err, req, res, next) {
      console.error(err);
      res.status(500).json({ message: "Something went wrong!" });
    }

3.3 错误处理中间件的注意事项

  • 错误处理中间件必须位于所有其他中间件和路由处理之后。
  • 错误处理中间件必须有四个参数:errreqresnext

4. 总结

中间件是应用架构中的重要组成部分,它们为应用的各个阶段提供了灵活的处理方式。设计中间件时,遵循 单一职责顺序性可重用性 等原则,能够有效提高代码的可维护性、可扩展性和可测试性。

中间件的主要应用场景

  • 认证与授权
  • 请求数据处理(如 JSON 解析、文件上传)
  • 错误捕获与日志记录
  • 性能监控与限流

通过合理的设计和使用中间件,可以大大提升系统的可扩展性和功能模块的解耦性。