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

如何使用Mongoose的pre中间件对findOneAndUpdate做数据类型校验

问题根因

  • 你开启的runValidators默认仅校验Schema中配置的必填、自定义校验规则等,Mongoose默认会对更新字段做静默类型转换,比如传入数字给String类型字段会自动转成字符串,不会触发错误
  • 你写的pre("save")钩子仅对save()操作生效,对findOneAndUpdate、updateMany等更新类查询不生效
  • 代码存在笔误:你定义的是User模型,但更新接口中调用的是Post模型,需要先修正

解决方案

方案1:开启类型转换失败抛错(最简实现)

Mongoose 5.11+版本支持给字段或整个Schema配置cast: 'throw',类型不匹配无法转换时直接抛出校验错误,配合你已开启的全局runValidators即可生效,修改Schema如下:

const UserSchema = new Schema ({
  nome: {
    type: String,
    required: true,
    trim: true,
    cast: 'throw' // 新增:类型转换失败直接抛错
  },
  email: {
    type: String,
    required: true,
    trim: true,
    cast: 'throw'
  },
  morada: {
    type: String,
    required: true,
    trim: true,
    cast: 'throw'
  }
}, {
  // 也可以全局配置给当前Schema所有字段生效,不需要每个字段单独加
  cast: 'throw'
});

方案2:使用findOneAndUpdate专属前置钩子(自定义校验逻辑)

如果需要自定义校验规则,直接针对findOneAndUpdate事件写前置钩子,注意更新钩子中要通过this.getUpdate()获取待更新的数据,而不是直接拿this(this在更新钩子中是查询实例,不是文档实例):

User.pre('findOneAndUpdate', function(next) {
  const updateData = this.getUpdate();
  // 兼容普通更新和带$set的更新写法
  const nome = updateData.$set?.nome ?? updateData.nome;
  const email = updateData.$set?.email ?? updateData.email;
  const morada = updateData.$set?.morada ?? updateData.morada;

  // 校验nome类型
  if (nome !== undefined && typeof nome !== 'string') {
    this.invalidate('nome', 'nome必须为字符串类型');
    return next(new Error('nome必须为字符串类型'));
  }
  // 同理校验其他字段
  if (email !== undefined && typeof email !== 'string') {
    this.invalidate('email', 'email必须为字符串类型');
    return next(new Error('email必须为字符串类型'));
  }
  if (morada !== undefined && typeof morada !== 'string') {
    this.invalidate('morada', 'morada必须为字符串类型');
    return next(new Error('morada必须为字符串类型'));
  }
  next();
});

额外优化建议

调用更新接口时建议显式开启runValidators(避免全局配置未生效的情况),同时建议明确指定查询条件,不要直接传整个req.params:

const update = async (req, res) => {
  try {
    // 显式指定查询条件、开启runValidators
    let user = await User.findOneAndUpdate(
      { _id: req.params.id }, 
      req.body, 
      { new: true, runValidators: true }
    );     
    res.json(user)
  } catch (e) {
    res.status(400).json({ message: e.message }) // 校验错误属于客户端参数错误,用400状态码更规范
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 20:27:01