Node| 如何创建一个自定义的验证中间件?

目录

[1. 基础结构:自定义中间件原理](#1. 基础结构:自定义中间件原理)

[2. 实战示例:手动编写验证中间件](#2. 实战示例:手动编写验证中间件)

步骤一:定义验证中间件

步骤二:在路由中使用

[3. 进阶方式:使用 express-validator 库](#3. 进阶方式:使用 express-validator 库)

[4. 高级技巧:可复用的验证工厂](#4. 高级技巧:可复用的验证工厂)

[✔ 最佳实践总结 ✔](#✔ 最佳实践总结 ✔)


在 Express.js 中创建自定义验证中间件,核心在于编写一个能够访问**req(请求对象)、res**(响应对象)和 next(下一个中间件函数)的函数。该函数负责检查请求数据,若验证失败则直接终止请求并返回错误,若验证通过则调用 next() 将控制权交给后续处理逻辑。

以下是创建和使用自定义验证中间件的完整指南:

1. 基础结构:自定义中间件原理

Express 中间件是一个标准的 JavaScript 函数,其签名如下:

javascript 复制代码
function middlewareName(req, res, next) {
  // 1. 执行验证逻辑
  // 2. 如果验证失败:res.status(400).json({ error: '...' })
  // 3. 如果验证成功:next()
}

‌ **关键点:**‌

  • 必须调用 next() ‌:如果验证通过,必须调用 next(),否则请求会挂起,客户端收不到响应。
  • 提前终止 ‌:如果验证失败,发送响应后‌不要 ‌调用 next(),以防止执行后续的业务逻辑。

2. 实战示例:手动编写验证中间件

假设我们需要验证用户注册接口,确保 username 和**email**字段存在且格式正确。

步骤一:定义验证中间件

你可以创建一个独立的函数或模块来封装验证逻辑。

javascript 复制代码
// middleware/validateUser.js
const validateUser = (req, res, next) => {
  const { username, email } = req.body;
  const errors = [];

  // 验证用户名
  if (!username || typeof username !== 'string' || username.trim().length < 3) {
    errors.push('Username is required and must be at least 3 characters long');
  }

  // 验证邮箱格式
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!email || !emailRegex.test(email)) {
    errors.push('Valid email address is required');
  }

  // 如果有错误,返回 400 状态码并停止后续执行
  if (errors.length > 0) {
    return res.status(400).json({
      status: 'error',
      message: 'Validation failed',
      details: errors
    });
  }

  // 验证通过,继续执行下一个中间件或路由处理器
  next();
};

module.exports = validateUser;
步骤二:在路由中使用

将中间件挂载到特定的路由上。注意中间件函数的顺序:验证中间件必须在业务逻辑处理函数之前。

javascript 复制代码
const express = require('express');
const app = express();
const validateUser = require('./middleware/validateUser');

// 解析 JSON 请求体
app.use(express.json());

// POST /register 路由
// 顺序:1. parse JSON -> 2. validateUser -> 3. handler
app.post('/register', validateUser, (req, res) => {
  // 此时可以确信 req.body.username 和 req.body.email 是有效的
  const { username, email } = req.body;
  
  // 模拟数据库操作
  console.log(`Creating user: ${username}, ${email}`);
  
  res.status(201).json({
    status: 'success',
    message: 'User registered successfully',
    data: { username, email }
  });
});

app.listen(3000, () => console.log('Server running on port 3000'));

3. 进阶方式:使用**express-validator**库

对于复杂项目,手动编写正则表达式和条件判断容易出错且难以维护。推荐使用业界标准的 express-validator 库,它基于 validator.js,提供了链式调用的验证规则和数据净化功能。

‌**安装:**‌

bash 复制代码
npm install express-validator

‌**实现代码:**‌

javascript 复制代码
const { body, validationResult } = require('express-validator');
const express = require('express');
const app = express();

app.use(express.json());

// 定义验证规则数组
const registerValidationRules = [
  body('username')
    .trim()
    .notEmpty().withMessage('Username is required')
    .isLength({ min: 3 }).withMessage('Username must be at least 3 characters'),
  
  body('email')
    .normalizeEmail()
    .isEmail().withMessage('Invalid email format'),
  
  body('password')
    .isLength({ min: 8 }).withMessage('Password must be at least 8 characters')
    .matches(/[0-9]/).withMessage('Password must contain a number')
];

// 通用错误处理中间件(可选,用于统一处理验证结果)
const handleValidationErrors = (req, res, next) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  next();
};

// 应用验证规则
app.post('/register', 
  registerValidationRules, // 1. 执行验证规则
  handleValidationErrors,  // 2. 检查验证结果
  (req, res) => {
    // 3. 业务逻辑
    res.json({ message: 'User created', data: req.body });
  }
);

app.listen(3000);

4. 高级技巧:可复用的验证工厂

为了进一步提高代码复用性,可以创建一个"工厂函数"来生成针对特定字段的验证中间件。

javascript 复制代码
const { body, validationResult } = require('express-validator');

// 创建一个通用的验证中间件生成器
const createValidator = (validations) => {
  return async (req, res, next) => {
    // 执行所有传入的验证规则
    await Promise.all(validations.map(validation => validation.run(req)));

    const errors = validationResult(req);
    if (errors.isEmpty()) {
      return next();
    }

    res.status(400).json({ errors: errors.array() });
  };
};

// 定义具体的验证规则
const userRegistrationValidations = [
  body('email').isEmail(),
  body('password').isLength({ min: 6 })
];

// 生成中间件
const validateRegistration = createValidator(userRegistrationValidations);

// 使用
app.post('/signup', validateRegistration, (req, res) => {
  res.send('Signup successful');
});

✔ 最佳实践总结 ✔

  1. 分离关注点‌:验证中间件只负责数据格式和完整性检查,不应包含数据库查询或业务逻辑(如"用户是否已存在"属于业务逻辑,但"邮箱格式是否正确"属于验证)。
  2. 统一错误格式‌:确保所有验证失败的响应具有相同的 JSON 结构,便于前端统一处理。
  3. 数据净化 ‌:在验证的同时进行数据清洗(如 trim() 去除空格,escape() 防止 XSS),express-validator 内置了这些功能。
  4. 异步支持 ‌:如果验证需要查询数据库(如检查用户名唯一性),中间件函数应声明为 async,并使用 await 等待异步操作完成。
  5. 位置安排 ‌:确保验证中间件位于 express.json() 之后,以便能访问到解析后的 req.body

通过上述方法,你可以构建出既安全又易于维护的请求验证层,有效保护你的 Express 应用免受无效或恶意数据的侵害。


☑ 相关参考 ☑

Node | 如何在Express.js中处理异步验证逻辑

Nodejs | 深入理解Express框架之如何使用各类中间件