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
相关产品推荐
相关产品推荐

