做服务端开发的小伙伴应该都有过这样的经历:一个项目里,每个接口都要打印请求日志、校验参数合法性、判断用户是否登录,重复的代码写了一遍又一遍,想改规则得找N个地方,改完还容易漏。用Node.js中间件就能完美解决这个问题——把通用的、重复的逻辑抽成可复用的模块,就像流水线的各个工位,请求进来后依次经过这些工位,最后到接口逻辑,既干净又好维护。

一、为什么服务端开发离不开中间件

1.1 中间件的通俗理解

你可以把Node.js中间件比作奶茶店的制作流程:客人点单后,先经过「取单登记」这个环节,再到「制作奶茶」的核心环节,最后「打包递餐」。这个「取单登记」的环节就是中间件,它只做登记这件事,不会干扰核心制作流程。对应到服务端,中间件就是处理请求的中间环节,每个中间件只做一件通用的事,所有请求都会经过这些环节。

1.2 中间件解决的核心痛点

刚接触Node.js的开发者很容易把日志、参数校验这类逻辑直接塞到每个接口里,小项目还行,项目变大后,几十个接口都要改校验规则,得一个个找过去,改完还怕漏,不仅效率低,还容易出bug。中间件刚好解决这个问题:把通用逻辑抽出来,做成可复用的模块,改一次就能让所有用到的接口生效,大大减少重复代码和维护成本。

二、手写第一个可复用的Node.js中间件

我们拿最常用的Express框架来举例,它对中间件的支持非常友好,适合新手练习。

2.1 准备工作

先初始化项目,安装Express框架,执行以下命令:

# 初始化npm项目,生成package.json配置文件
npm init -y
# 安装Express框架
npm install express

2.2 实现请求日志中间件

这个中间件的作用是打印每个请求的访问信息,比如请求时间、方法、路径、响应状态码和处理耗时,所有接口都能复用,不用重复写代码,实现代码如下:

// 技术栈:Node.js + Express(单一技术栈,无混合使用其他工具)
const express = require('express');
const app = express();
// 定义请求日志中间件函数,接收请求对象、响应对象、下一个环节的回调
const requestLogger = (req, res, next) => {
  // 记录请求开始时间,用来计算处理耗时
  const requestStartTime = Date.now();
  // 监听响应完成事件,等接口处理完后打印日志
  res.on('finish', () => {
    const duration = Date.now() - requestStartTime;
    // 打印格式化日志,方便排查问题
    console.log(`[${new Date().toLocaleString()}] ${req.method} ${req.path} | 状态码:${res.statusCode} | 耗时:${duration}ms`);
  });
  // 必须调用next(),否则请求会卡住,不会进入后续逻辑——这是新手最容易踩的坑!
  next();
};
// 把中间件注册到Express应用,所有进入该应用的请求都会先经过这个中间件
app.use(requestLogger);
// 写一个测试接口,验证中间件是否生效
app.get('/api/test', (req, res) => {
  res.send('Hello,Node.js中间件!');
});
// 启动服务,监听3000端口
app.listen(3000, () => {
  console.log('服务已启动,访问地址:http://localhost:3000');
});

把代码保存为app.js,执行node app.js启动服务,访问测试地址后,就能在控制台看到请求日志,说明中间件生效了。如果再写一个用户列表接口,只要在中间件注册后,日志会自动记录,不用再写一遍日志逻辑,这就是可复用的价值。

三、实战:通用参数校验中间件开发

刚才的日志中间件是全局的,所有请求都用;那如果要针对特定接口做参数校验,比如登录接口,总不能每个接口都写校验逻辑吧?我们可以做一个通用的参数校验中间件,只要传入校验规则,就能给任意接口复用。

3.1 场景分析

登录、注册、修改资料这类接口,都需要校验参数:手机号必须是11位有效格式,密码至少6位。如果把校验逻辑写在每个接口里,以后改规则要改N个地方;做成中间件后,改一次就生效,还能复用。

3.2 技术选型

还是用Node.js+Express,再加上参数校验库Joi,简化校验逻辑,安装命令如下:

# 安装Joi参数校验库
npm install joi

3.3 实现通用参数校验中间件

代码如下:

// 技术栈:Node.js + Express + Joi
const express = require('express');
const Joi = require('joi');
const app = express();
// 通用参数校验中间件,接收Joi定义的校验规则作为参数
const validateParams = (schema) => (req, res, next) => {
  // 校验请求体里的参数,如果是GET请求则校验query参数
  const validateResult = schema.validate(req.body);
  if (validateResult.error) {
    // 校验失败,返回清晰的错误信息给前端
    return res.status(400).json({
      code: 400,
      msg: `参数错误:${validateResult.error.details[0].message}`
    });
  }
  // 校验通过,进入后续业务逻辑
  next();
};
// 定义登录接口的校验规则:手机号必须是11位有效格式,密码至少6位
const loginSchema = Joi.object({
  phone: Joi.string().pattern(/^1[3-9]\d{9}$/).required().messages({
    'string.pattern.base': '手机号格式不正确,请输入11位中国大陆手机号',
    'any.required': '手机号不能为空'
  }),
  password: Joi.string().min(6).required().messages({
    'string.min': '密码至少需要6位',
    'any.required': '密码不能为空'
  })
});
// 注册登录接口,把校验中间件加到路由上,只有通过校验才能进入业务逻辑
app.post('/api/login', validateParams(loginSchema), (req, res) => {
  // 这里只写核心的登录业务逻辑,不用再处理参数校验
  res.json({ code: 200, msg: '登录成功' });
});
// 启动服务
app.listen(3000);

这个中间件的优势很明显:以后写注册接口,只要定义注册的校验规则,再把中间件传入新规则,就能直接复用,不用再写重复的校验代码。

四、中间件的核心要点梳理

4.1 主要应用场景

中间件的应用非常广泛,常见场景包括:

  1. 请求日志:记录所有请求的情况,方便排查问题;
  2. 参数校验:确保接口参数合法,避免业务逻辑出错;
  3. 权限校验:判断用户是否登录,解析token获取用户信息,不用每个接口都解析;
  4. 错误处理:统一返回错误格式,不用每个接口都写try-catch;
  5. 跨域处理:允许指定域名访问接口,不用每个接口设置响应头。

4.2 技术优缺点分析

优点:

  1. 复用性极强:一个中间件可用于多个项目、多个接口,减少重复代码;
  2. 维护方便:修改中间件逻辑,所有用到的地方自动更新,不用逐个修改;
  3. 分层清晰:通用逻辑和业务逻辑分离,业务开发者只关心核心功能;
  4. 安全性统一:权限、参数校验这类规则统一在中间件,避免漏加导致安全问题。 缺点:
  5. 顺序敏感:中间件顺序错了会出问题,比如日志要放权限校验前面,否则拦截的请求不会被记录;
  6. 过多中间件增加耗时:逻辑复杂的中间件会延长请求处理时间;
  7. 新手易踩坑:忘写next()会导致请求卡住,顺序错了功能失效。

4.3 开发注意事项

  1. 必须调用next():这是核心规则,中间件不调用next()请求会一直挂起;
  2. 中间件顺序:全局中间件(日志、跨域)放最前面,路由级中间件(权限校验)放对应路由前面,错误处理中间件放最后;
  3. 单一职责:一个中间件只做一件事,比如不要把日志和权限校验放一起,方便复用;
  4. 可配置性:尽量把中间件做成可配置的,比如日志中间件可以配置是否脱敏密码;
  5. 异常处理:要考虑参数不存在、规则出错等异常情况,避免服务端崩溃。

五、实战总结与扩展思考

通过两个实战示例,我们可以看到Node.js中间件的核心价值:把通用、重复的逻辑抽成可复用的模块,让服务端开发更高效、代码更干净。写中间件的思路很简单:先找到项目里重复的代码段,封装成可配置的函数,最后在需要的地方注册使用。 如果要扩展,我们可以把写好的中间件做成npm包分享给其他项目,或者根据环境变量配置中间件的开关(比如开发环境打印详细日志,生产环境简化日志)。掌握中间件开发,能帮你打造更优雅、易维护的Node.js服务端项目,大幅提升开发效率。