express中间件概述以及中间件的设计理念

107 阅读2分钟
  1. 基本概念
  • 中间件是一个函数,可以访问请求对象(req)、响应对象(res)和next函数
  • 按照顺序执行,从上到下依次调用
  • 可以执行任何代码、修改请求和响应对象、结束请求-响应循环、调用下一个中间件。如果当前中间件没有终结请求,并且next()没有被调用,那么请求将被挂起,后面定义的中间件将不会被执行。
  • 中间件的主要用途就是处理HTTP请求,用来完成特定的任务如登录状态验证、请求日志、错误处理、Cookie等。
  1. 中间件类型


// 应用级中间件
app.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

// 路由级中间件
app.use('/user', (req, res, next) => {
  // 只处理/user路径的请求
  next();
});

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

// 内置中间件
app.use(express.json()); // 解析JSON
app.use(express.static('public')); // 静态文件

// 第三方中间件
app.use(cors()); // 处理跨域
  1. 执行流程示例


app.use((req, res, next) => {
  console.log('1. 首先经过这个中间件');
  next(); // 调用下一个中间件
});

app.use((req, res, next) => {
  console.log('2. 然后是这个中间件');
  next();
});

app.get('/api', (req, res) => {
  console.log('3. 最后到达路由处理器');
  res.send('Hello');
});
  1. 常见使用场景


// 身份验证
app.use((req, res, next) => {
  if (!req.headers.authorization) {
    return res.status(401).json({ message: '未授权' });
  }
  next();
});

// 日志记录
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

// 错误处理
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).send('服务器错误');
});
  1. 自定义中间件示例


// 计时中间件
const timeMiddleware = (req, res, next) => {
  req.startTime = Date.now();
  next();
  console.log(`请求处理时间: ${Date.now() - req.startTime}ms`);
};

// 使用中间件
app.use(timeMiddleware);

image.png

为什么这么设计?

将请求处理函数进行模块化分解后的若干子处理函数,一些列子处理函数可以形成一个中间件堆栈



// Request flow through middleware chain
app.use(loggerMiddleware)  // 1. Log request
   .use(authMiddleware)    // 2. Check auth
   .use(parseJsonBody)     // 3. Parse body
   .get('/api', handler);  // 4. Handle route

好处:

  • 模块化: 每个中间件只负责单一功能
  • 可复用: 中间件可在不同路由复用
  • 灵活性: 可根据需求动态添加/移除中间件
  • 维护性: 职责分离便于维护和测试
  • 扩展性: 易于添加新功能而不影响现有代码

职责分离例子:



// Authentication middleware
const auth = (req, res, next) => {
  verifyToken(req.headers.token)
    .then(() => next())
    .catch(() => res.status(401).send());
}

// Business logic stays clean
app.get('/api', auth, (req, res) => {
  // Only handle business logic here
  res.json(data);
});

错误处理链式传递:



app.use((err, req, res, next) => {
  if (err.type === 'auth') {
    res.status(401).send();
  } else {
    next(err); // Pass to next error handler
  }
});

这种设计模式让代码更容易维护、测试和扩展,同时保持了良好的关注点分离。