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

解决products模型_id字段Cast to ObjectId转换失败问题

问题:获取单个产品数据时出现ObjectId转换错误

错误信息

Cast to ObjectId failed for value "[object Object]" (type string) at path "_id" for model "products".

相关代码

后端代码

productController.jsx

const formidable = require("formidable");
const { responseReturn } = require("./../../utiles/response");
const cloudinary = require("cloudinary").v2;
const productModel = require("./../../models/productModels");

class productController {
    add_product = async (req, res) => {
        const { id } = req;

        const form = formidable({ multiples: true });

        form.parse(req, async (err, field, files) => {
            let { name, category, brand, price, stock, discount, description, shopName } = field;
            const { images } = files;

            name = name.trim();
            const slug = name.split(" ").join("-");

            cloudinary.config({
                cloud_name: process.env.cloud_name,
                api_key: process.env.api_key,
                api_secret: process.env.api_secret,
                secure: true,
            });

            try {
                let allImageUrl = [];
                for (let index = 0; index < images.length; index++) {
                    const result = await cloudinary.uploader.upload(images[index].filepath, { folder: "products" });
                    allImageUrl = [...allImageUrl, result.url];
                }

                await productModel.create({
                    sellerId: id,
                    name,
                    slug,
                    shopName,
                    category: category.trim(),
                    description: description.trim(),
                    stock: parseInt(stock),
                    price: parseInt(price),
                    discount: parseInt(discount),
                    brand: brand.trim(),
                    images: allImageUrl,
                });

                responseReturn(res, 201, { message: "Product Added Successfully" });
            } catch (error) {
                console.error("Error adding product:", error);
                responseReturn(res, 500, { error: "Internal Server Error" });
            }
        });
    };

    products_get = async (req, res) => {
        const { page, searchValue, parPage } = req.query;
        const { id } = req;

        const skipPage = parseInt(parPage) * (parseInt(page) - 1);

        try {
            if (searchValue) {
                const products = await productModel
                    .find({
                        $text: { $search: searchValue },
                        sellerId: id,
                    })
                    .skip(skipPage)
                    .limit(parPage)
                    .sort({ createdAt: -1 });
                const totalProduct = await productModel
                    .find({
                        $text: { $search: searchValue },
                        sellerId: id,
                    })
                    .countDocuments();
                responseReturn(res, 200, { products, totalProduct });
            } else {
                const products = await productModel
                    .find({ sellerId: id })
                    .skip(skipPage)
                    .limit(parPage)
                    .sort({ createdAt: -1 });
                const totalProduct = await productModel.find({ sellerId: id }).countDocuments();
                responseReturn(res, 200, { products, totalProduct });
            }
        } catch (error) {
            console.log(error);
        }
    };

    product_get = async (req, res) => {
        const { productId } = req.params;
        try {
            const product = await productModel.findById(productId);
            responseReturn(res, 200, { product });
        } catch (error) {
            console.log(error.message);
        }
    };
}

module.exports = new productController();

productRoutes.js

const productController = require("./../../controllers/dashboard/productController");
const { authMiddleware } = require("../../middlewares/authMiddleware");
const router = require("express").Router();

router.post("/product-add", authMiddleware, productController.add_product);
router.get("/products-get", authMiddleware, productController.products_get);
router.get("/product-get/:productId", authMiddleware, productController.product_get);

module.exports = router;

前端代码

EditProduct.jsx

import React, { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { get_category } from "./../../store/Reducers/categoryReducer";
import { get_product } from "./../../store/Reducers/productReducer";

function EditProduct() {
    const { productId } = useParams();
    const dispatch = useDispatch();
    const { categories } = useSelector((state) => state.category);
    const { product } = useSelector((state) => state.product);

    useEffect(() => {
        dispatch(
            get_category({
                searchValue: "",
                parPage: "",
                page: "",
            })
        );
    }, []);

    useEffect(() => {
        dispatch(
            get_product({ productId })
        );
    }, [productId]);

    const [categoryShow, setCategoryShow] = useState(false);
    const [category, setCategory] = useState("");
    const [allCategory, setAllCategory] = useState(categories);
    const [searchValue, setSearchValue] = useState("");

    const categorySearch = (event) => {
        const value = event.target.value;
        setSearchValue(value);
        if (value) {
            let searchValue = allCategory.filter(
                (category) => category.name.toLowerCase().indexOf(value.toLowerCase()) > -1
            );
            setAllCategory(searchValue);
        } else {
            setAllCategory(categories);
        }
    };

    const [state, setState] = useState({
        name: "",
        description: "",
        discount: "",
        price: "",
        brand: "",
        stock: "",
    });

    const inputHandle = (event) => {
        setState({
            ...state,
            [event.target.name]: event.target.value,
        });
    };

    const [images, setImages] = useState([]);
    const [imageShow, setImageShow] = useState([]);

    const changeImage = (img, files) => {
        if (files.length > 0) {
            console.log(files[0]);
            console.log(img);
        }
    };

    useEffect(() => {
        setState({
            name: product.name,
            description: product.description,
            discount: product.discount,
            price: product.price,
            brand: product.brand,
            stock: product.stock,
        });
        setCategory(product.category);
        setImageShow(product.images);
    }, [product]);
}

Product.jsx(代码片段)

<Link
 to={`/seller/dashboard/edit-product/${product._id}`}                                              className="p-[6px] bg-yellow-500 rounded hover:shadow-lg hover:shadow-yellow-500/50">{''}
 <FaEdit />{''}
</Link>

productReducer.js

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import api from "../../api/api";

export const get_product = createAsyncThunk(
    "product/get_product",
    async (productId, { rejectWithValue, fulfillWithValue }) => {
        try {
            const { data } = await api.get(`/product-get/${productId}`, { withCredentials: true });
            console.log(data);
            return fulfillWithValue(data);
        } catch (error) {
            return rejectWithValue(error.response.data);
        }
    }
);

export const productReducer = createSlice({
    name: "product",
    initialState: {
        successMessage: "",
        errorMessage: "",
        loader: false,
        userInfo: "",
        products: [],
        product: "",
        totalProduct: 0,
    },
    reducers: {
        messageClear: (state, _) => {
            state.errorMessage = "";
        },
    },
    extraReducers: (builder) => {
        builder
            .addCase(add_product.pending, (state, { payload }) => {
                state.loader = true;
            })
            .addCase(add_product.rejected, (state, { payload }) => {
                state.loader = false;
                state.errorMessage = payload.error;
            })
            .addCase(add_product.fulfilled, (state, { payload }) => {
                state.loader = false;
                state.successMessage = payload.message;
            })
            .addCase(get_products.fulfilled, (state, { payload }) => {
                state.totalProduct = payload.totalProduct;
                state.products = payload.products;
            })
            .addCase(get_product.fulfilled, (state, { payload }) => {
                state.product = payload.product;
            });
    },
});

export const { messageClear } = productReducer.actions;
export default productReducer.reducer;

错误原因

核心问题出在前端Redux异步请求的传参错误:

  • 在EditProduct.jsx中,调用dispatch(get_product({ productId }))时,把productId包装成了对象传入
  • 但productReducer.js里的get_product异步thunk期望接收字符串类型的productId,实际收到的是{ productId: "xxx" }这个对象
  • 拼接API URL时,对象被强制转换为字符串"[object Object]",后端接收到这个无效值后,MongoDB无法将其转换为合法的ObjectId,从而抛出错误

解决方案

1. 修正前端传参方式

在EditProduct.jsx的useEffect中,直接传入productId字符串,不要包装成对象:

useEffect(() => {
    dispatch(get_product(productId)); // 去掉外层的{}
}, [productId]);

2. (可选)后端增加参数校验

为避免类似错误,在productController.jsx的product_get方法中,先校验productId是否为合法的ObjectId:

const mongoose = require('mongoose');

product_get = async (req, res) => {
    const { productId } = req.params;
    try {
        // 校验ObjectId合法性
        if (!mongoose.Types.ObjectId.isValid(productId)) {
            return responseReturn(res, 400, { error: "无效的产品ID" });
        }
        const product = await productModel.findById(productId);
        if (!product) {
            return responseReturn(res, 404, { error: "产品不存在" });
        }
        responseReturn(res, 200, { product });
    } catch (error) {
        console.log(error.message);
        responseReturn(res, 500, { error: "服务器内部错误" });
    }
};

3. 确认路由参数传递

当前Product.jsx中的链接参数传递是正确的,无需修改:

<Link to={`/seller/dashboard/edit-product/${product._id}`}>
    <FaEdit />
</Link>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 06:29:56