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

Mongoose添加商品评论时rating字段NaN类型转换失败问题求助

商品评论添加时的ValidationError问题排查与修复

问题描述

给关联商品添加评论时,通过Postman调用API始终报错,错误信息:

ValidationError: Product validation failed: rating: Cast to Number failed for value "NaN" (type number) at path "rating"

错误栈信息

ValidationError: Product validation failed: rating: Cast to Number failed for value "NaN" (type number) at path "rating"
[0]     at Document.invalidate (E:\Web\Shop.co\node_modules\mongoose\lib\document.js:3197:32)
[0]     at model.$set (E:\Web\Shop.co\node_modules\mongoose\lib\document.js:1456:12)
[0]     at model.set [as rating] (E:\Web\Shop.co\node_modules\mongoose\lib\helpers\document\compile.js:205:19)
[0]     at E:\Web\Shop.co\backend\controller\productController.js:108:20
[0]     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

相关代码

Product Schema代码

const mongoose = require("mongoose");

const reviewSchema = mongoose.Schema(
  {
    name: { type: String, required: true },
    rating: { type: Number, required: true, default: 0 },
    comment: { type: String, required: true },
    user: {
      type: mongoose.Schema.Types.ObjectId,
      required: true,
      ref: "User",
    },
  },
  {
    timestamps: true,
  }
);

const productSchema = mongoose.Schema(
  {
    user: {
      type: mongoose.Schema.Types.ObjectId,
      required: true,
      ref: "User",
    },

    name: {
      type: String,
      required: true,
    },
    image: {
      type: String,
      required: true,
    },
    brand: {
      type: String,
      required: true,
    },
    category: {
      type: String,
      required: true,
    },
    description: {
      type: String,
      required: true,
    },
    price: {
      type: Number,
      required: true,
      default: 0,
    },
    review: [reviewSchema],
    rating: {
      type: Number,
      required: true,
      default: 0,
    },
    numReviews: {
      type: Number,
      required: true,
      default: 0,
    },
    countInStock: {
      type: Number,
      required: true,
      default: 0,
    },
  },
  {
    timestamps: true,
  }
);

const Product = mongoose.model("Product", productSchema);

module.exports = Product;

控制器createReview函数代码

const createReview = asyncHandler(async (req, res) => {
  const { rating, comment } = req.body;
  const product = await Product.findById(req.params.id);
  if (product) {
    const alreadyReviewed = product.review.find(
      (r) => r.user.toString() === req.user._id.toString()
    );

    if (alreadyReviewed) {
      res.status(400).json({ message: " Product already reviewed" });
      throw new Error("Product already reviewed");
    }

    const reviews = {
      name: req.user.name,
      rating: Number(rating),
      comment,
      user: req.user._id,
    };
    product.review.push(reviews);

    product.numReviews = product.review.length;
    product.rating =
      product.review.reduce((item, acc) => item.rating + acc, 0) /
      product.review.length;

    await product.save();
    res.status(100).json({ message: " Review Added" });
  } else {
    res.status(400).json({ message: " Product not found" });
    throw new Error("Product not found");
  }
});

路由代码

router.route("/:id/reviews").post(protect, createReview);

错误原因分析

  1. reduce参数顺序错误:计算商品平均评分时,reduce回调函数的参数顺序写反了。正确顺序应为(acc, item)(累加器在前,当前元素在后),但代码中写成(item, acc),导致第一次累加时0 + 评论对象得到NaN,最终product.rating为NaN,触发Mongoose类型验证错误。
  2. 缺少输入校验:未对req.body.rating做有效性校验,如果前端未传该字段或传入非数字值,Number(rating)会转为NaN,直接存入评论的rating字段,后续计算也会出错。

修复方案

1. 修正reduce参数顺序

修改控制器中平均评分计算代码:

product.rating =
  product.review.reduce((acc, item) => acc + item.rating, 0) /
  product.review.length;

2. 添加输入有效性校验

在创建评论前校验rating是否为有效数字:

const createReview = asyncHandler(async (req, res) => {
  const { rating, comment } = req.body;
  
  // 新增输入校验
  if (!rating || isNaN(Number(rating)) || Number(rating) < 1 || Number(rating) > 5) {
    res.status(400).json({ message: "Invalid rating value, must be a number between 1 and 5" });
    throw new Error("Invalid rating value");
  }
  
  const product = await Product.findById(req.params.id);
  // 后续代码不变...
});

3. 处理空评论列表边界情况(可选)

预防无评论时除以0的情况:

product.rating = product.review.length === 0 
  ? 0 
  : product.review.reduce((acc, item) => acc + item.rating, 0) / product.review.length;

4. 修正HTTP状态码

  • 评论添加成功返回201 Created,而非100
  • 商品不存在返回404 Not Found,而非400 Bad Request

修改后的响应代码:

// 评论添加成功
res.status(201).json({ message: "Review Added" });

// 商品不存在
res.status(404).json({ message: "Product not found" });

改进建议

  • 使用Joi或express-validator做完善的请求参数校验,覆盖必填字段的格式、范围校验
  • 在Schema中给评论的rating字段添加范围限制:
    rating: { type: Number, required: true, default: 0, min: 1, max: 5 },
    
  • 提取平均评分计算逻辑为单独函数,提高代码复用性
  • 保存商品时添加错误捕获,返回更友好的错误信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 14:05:42