Published on

Express 项目的中间件汇总

Authors
  • avatar
    Name
    Shelton Ma
    Twitter

1. 中间件概述

在 Express 中,中间件是处理请求和响应流程中的核心部分.根据执行时机,可以将中间件分为前中间件(前置中间件)和后中间件(后置中间件).

1. 前置中间件(Pre Middleware)

  1. 前置中间件在请求处理开始时执行,通常用于:

    • 参数验证

    • 权限验证

    • 日志记录

    • 请求体解析

    • 设置响应头

    • 静态资源服务

    • 速率限制(防止 API 滥用,限制请求速率)

      import rateLimit from 'express-rate-limit';
      
      export const rateLimiter = rateLimit({
        windowMs: 15 * 60 * 1000, // 15分钟窗口
        max: 100,                 // 每个IP最多100个请求
        standardHeaders: true,    // 返回 `RateLimit-*` 头信息
        legacyHeaders: false,     // 禁用 `X-RateLimit-*` 头信息
        message: {
          success: false,
          error: 'Too many requests, please try again later.'
        },
        handler: (req, res) => {
          res.status(429).json({
            success: false,
            error: 'Too many requests, please try again later.'
          });
        }
      });
      
    • API 请求追踪(记录请求耗时、状态码等,便于追踪性能瓶颈)

      import { Request, Response, NextFunction } from 'express';
      export function trackPerformance(req: Request, res: Response, next: NextFunction) {
        const start = process.hrtime(); // 高精度计时器
      
        res.on('finish', () => {
          const [seconds, nanoseconds] = process.hrtime(start);
          const durationInMs = (seconds * 1000 + nanoseconds / 1e6).toFixed(2);
      
          logger.info({
            method: req.method,
            path: req.originalUrl,
            statusCode: res.statusCode,
            duration: `${durationInMs}ms`
          });
        });
      
        next();
      }
      
  2. 比如:

    import express from 'express';
    import pinoHttp from 'pino-http';
    import cors from 'cors';
    import helmet from 'helmet';
    
    const app = express();
    
    // 前中间件
    app.use(rateLimiter);         // 防止恶意请求
    app.use(trackPerformance);    // 性能监控
    
    app.use(express.json()); // 解析 JSON 请求体
    app.use(express.urlencoded({ extended: true }));
    app.use(pinoHttp());
    app.use(helmet());
    app.use(cookieParser());
    app.use(compression());
    
    // 自定义cors
    // app.use(cors());
    app.use(cors({
      origin: 'https://example.com', // 仅允许指定来源
      methods: ['GET', 'POST'], // 允许的请求方法
      allowedHeaders: ['Content-Type', 'Authorization'], // 允许的请求头
      credentials: true, // 允许携带 cookies
      optionsSuccessStatus: 200 // 一些旧版浏览器使用 204 状态时可能会有问题
    }));
    
    // 自定义验证中间件 参数验证、权限校验、白名单检查 
    app.use(checkAuth);
    
    app.use((req, res, next) => {
      console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
      next(); // 继续传递请求
    });
    
    // 路由
    app.get('/api/data', (req, res) => {
      res.json({ message: 'Hello World' });
    });
    
    // 错误处理中间件
    app.use((err, req, res, next) => {
      console.error('Error:', err);
      res.status(500).json({ error: 'Internal Server Error' });
    });
    
    app.listen(3000, () => console.log('Server running on port 3000'));
    

2. 后置中间件(Post Middleware)

  1. 后置中间件在请求处理完成后执行,主要用于:

    • 统一格式化响应数据
    • 日志记录
    • 错误处理
  2. 常见后置中间件示例

    中间件作用示例代码
    统一响应格式化格式化成功响应,返回统一结构app.use(responseFormatter);
    错误处理器 (err 参数)捕获并处理路由或中间件中的异常app.use(errorHandler);
    日志记录记录响应结果、错误信息等app.use(logErrorHandler);
  3. demo

    // 统一响应格式中间件
    function responseFormatter(req, res, next) {
      const originalJson = res.json;
      res.json = function (data) {
        originalJson.call(this, {
          success: true,
          data,
          timestamp: new Date().toISOString(),
        });
      };
      next();
    }
    
    export function logErrorHandler(err: Error, req: Request, res: Response, next: NextFunction) {
      logger.error({
        message: err.message,
        stack: err.stack,
        method: req.method,
        path: req.originalUrl
      });
    
      // 继续交由 `errorHandler` 处理响应
      next(err);
    }
    
    function errorHandler(err, req, res, next) {
      console.error(err);
      res.status(500).json({
        success: false,
        error: err.message,
      });
    }
    
    app.use(responseFormatter);
    app.use(logErrorHandler);
    app.use(errorHandler);
    

3. 中间件执行顺序

在 Express 中,中间件的执行顺序遵循以下规则:

  • app.use() 定义的中间件按声明顺序执行.
  • 匹配的路由处理完毕后,才会执行后续的后中间件(如统一格式化、错误处理).
  • next() 控制流程,如果未调用 next(),后续中间件将不会执行.

4. 最佳实践

  • 尽量将 参数校验、权限验证 放在前中间件,避免不必要的业务逻辑执行.

  • 使用 res.locals 在前中间件和后中间件之间共享数据,减少重复查询或计算.

  • 错误处理逻辑 作为单独的后中间件,确保所有异常都能被捕获.

  • 项目结构:

    /project
    ├── /middlewares
    │    ├── authMiddleware.ts
    │    ├── loggerMiddleware.ts
    │    ├── errorHandler.ts
    │    └── responseFormatter.ts
    ├── /routes
    │    ├── userRoutes.ts
    │    ├── orderRoutes.ts
    │    └── productRoutes.ts
    ├── app.ts
    └── server.ts