You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express Validator如何校验分类标题不存在以允许创建分类

Express Validator 实现分类标题唯一性校验

1. 修正并完善Mongoose分类模型

你原代码里timestamps: true写在了字段定义对象中,实际不会生效,需要挪到Schema的第二个配置参数里;同时给title字段加数据库层面的唯一索引做兜底,避免高并发场景下校验被绕过产生重复数据:

const mongoose = require('mongoose');
const CategorySchema = new mongoose.Schema({
  title: {
    type: String,
    required: [true, '分类标题不能为空'],
    unique: true,
    trim: true
  },
  dishes: [{ type: mongoose.Types.ObjectId }],
}, {
  timestamps: true,
  versionKey: false,
});
// 注意:如果你的数据库里已经存在重复的title字段值,需要先清理重复数据再启动服务,否则唯一索引会创建失败
const Category = mongoose.model('Category', CategorySchema);
module.exports = Category;

2. 编写Express Validator自定义校验规则

在创建分类的校验链中,通过custom()方法定义异步校验逻辑,查询数据库判断当前提交的标题是否已存在:

const { body, validationResult } = require('express-validator');
const Category = require('./models/category.model'); // 替换为你项目中分类模型的实际路径

// 分类创建接口的校验中间件
const createCategoryValidate = [
  body('title')
    .notEmpty()
    .withMessage('分类标题不能为空')
    .isString()
    .withMessage('分类标题必须为字符串格式')
    .trim()
    .isLength({ min: 1, max: 30 })
    .withMessage('分类标题长度需控制在1-30个字符之间')
    .custom(async (submitTitle) => {
      const hasExist = await Category.findOne({ title: submitTitle });
      if (hasExist) {
        throw new Error('分类标题已存在,请勿重复创建');
      }
      return true;
    })
];

3. 接口层处理校验结果

写一个通用的校验结果处理中间件,校验不通过时直接返回错误信息,校验通过再进入实际的业务逻辑:

// 通用校验错误处理中间件,所有使用express-validator的接口都可复用
const handleValidateError = (req, res, next) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({
      code: 400,
      msg: errors.array()[0].msg // 取第一个校验错误返回,也可根据业务需求返回全量错误
    });
  }
  next();
};

// 分类创建接口实际路由
app.post('/api/category/create', createCategoryValidate, handleValidateError, async (req, res) => {
  try {
    // 走到这里说明所有校验已通过,直接执行创建逻辑
    const newCategory = await Category.create({
      title: req.body.title
    });
    res.status(201).json({
      code: 201,
      msg: '分类创建成功',
      data: newCategory
    });
  } catch (error) {
    // 兜底捕获数据库层面抛出的唯一键重复错误,覆盖并发请求的极端场景
    if (error.code === 11000) {
      return res.status(400).json({
        code: 400,
        msg: '分类标题已存在,请勿重复创建'
      });
    }
    res.status(500).json({ code: 500, msg: '服务内部错误' });
  }
});

关键注意事项

  • 不要只依赖Express Validator的应用层校验,数据库层面的唯一索引必须加,否则两个同时到达的创建请求都查不到重复数据时,会插入两条重复记录
  • 校验前要对title字段做trim()处理,避免前后携带空格的同名分类绕过校验
  • 如果分类存在归属关系(比如属于某个商家、某个门店),查询重复时要加上归属条件,例如Category.findOne({ title: submitTitle, merchantId: req.user.merchantId }),避免误判其他主体下的同名分类为重复

内容的提问来源于stack exchange,提问作者Mikel Diaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 09:30:46