Express+Mongoose中/hello路由出现ObjectId转换错误求助
问题:GET /products/hello 路由触发CastError
使用本地MongoDB、Node.js、Express和Mongoose开发,其余路由均正常,唯独GET方法的/products/hello路由出现如下错误:
{ "stringValue": "\"hello\"", "valueType": "string", "kind": "ObjectId", "value": "hello", "path": "_id", "reason": {}, "name": "CastError", "message": "Cast to ObjectId failed for value \"hello\" (type string) at path \"_id\" for model \"product\"" }
将该路由改为POST方法可正常工作,改回GET方法仍报错,即使删除该路由错误依然存在。
相关代码
index.js
import mongoose from "mongoose"; import express, { json } from "express"; import 'dotenv/config' import productRouter from './routes/productR.js' const app = express() const connectionToDB = async () => { try { await mongoose.connect('mongodb://127.0.0.1:27017/testingDB') console.log('Connected to mongoDB'); } catch (error) { console.log(error); } } app.use(json()) app.use('/products', productRouter) app.listen(process.env.PORT, ()=>{ connectionToDB() console.log('Server in up at '+ process.env.PORT); })
product模型(productM.js)
import mongoose from "mongoose"; const productSchema = new mongoose.Schema({ name:{ type:String }, brand:{ type: String, }, size:{ type:[String], }, desc:{ type:String }, price:{ type:Number }, category:{ type:String }, picture:{ type:String } }) export default mongoose.model('product',productSchema)
product路由(productR.js)
import express from "express"; import { addProduct, deleteProduct, getProduct, getProducts, updateProduct } from "../controllers/productC.js"; const router = express.Router() router.post('/',addProduct) router.put('/:id',updateProduct) router.delete('/:id',deleteProduct) router.get('/:id',getProduct) router.get('/',getProducts) router.get("/hello",(req, res)=>{ res.json('5464') }) export default router;
product控制器(productC.js)
import Product from '../models/productM.js' export const addProduct = async (req, res)=>{ const newProduct = Product(req.body) try { const savedProduct = await newProduct.save() res.status(200).json(savedProduct) } catch (error) { res.status(500).json(error) } } export const updateProduct = async (req, res) => { try { const updatedProduct = await Product.findById(req.params.id) if(req.body.name) { updatedProduct.name = req.body.name} if(req.body.brand) { updatedProduct.brand = req.body.brand} if(req.body.size) { updatedProduct.size =(req.body.size)} if(req.body.desc) { updatedProduct.desc = req.body.desc} if(req.body.price) { updatedProduct.price = req.body.price} if(req.body.picture) { updatedProduct.picture = req.body.picture} if(req.body.category) { updatedProduct.category = req.body.category} await updatedProduct.save() res.status(200).json(updatedProduct) } catch (error) { res.status(500).json(error) } } export const deleteProduct = async (req, res) => { try { await Product.findByIdAndDelete(req.params.id) res.status(200).json(`The product (${req.params.id}) has been deleted`) } catch (error) { res.status(500).json(error) } } export const getProduct = async (req, res) => { const productID = req.params.id try { const foundProduct = await Product.findById(productID) res.status(200).json(foundProduct) } catch (error) { res.status(500).json(error) } } export const getProducts = async (req, res) => { const {min,max, ...others} = req.query const name = req.query.name || ""; const category = req.query.category || ""; const brand = req.query.brand || ""; console.log(name); try { const foundProducts = await Product.find({$and:[ { price:{$gte:parseInt(min) || 1, $lte:parseInt(max) || 9999}}, {name: {$regex: `${name}` || "" , $options: 'i' }}, {brand: {$regex: `${brand}` || "" , $options: 'i' }}, {category: {$regex: `${category}` || "" , $options: 'i' }} ]}) res.status(200).json(foundProducts) } catch (error) { res.status(500).json(error) } }
原因与解决方案
原因
Express路由按定义顺序匹配。当前代码中router.get('/:id', getProduct)在router.get('/hello', ...)之前,请求/products/hello时,Express会把hello当作:id参数传入getProduct控制器。Mongoose的findById方法会尝试将这个字符串转换为MongoDB ObjectId,而hello不是合法的ObjectId格式,因此抛出CastError。
改为POST方法能正常工作,是因为POST路由中没有定义/:id,不存在匹配冲突;删除/hello路由后错误依然存在,是因为/products/hello仍会被/:id路由捕获,触发同样的转换错误。
解决方案
调整路由顺序,将静态路由(如/hello)放在动态参数路由(如/:id)之前,让Express优先匹配静态路由:
修改后的product路由代码:
import express from "express"; import { addProduct, deleteProduct, getProduct, getProducts, updateProduct } from "../controllers/productC.js"; const router = express.Router() router.post('/',addProduct) router.put('/:id',updateProduct) router.delete('/:id',deleteProduct) // 静态路由提前 router.get("/hello",(req, res)=>{ res.json('5464') }) router.get('/:id',getProduct) router.get('/',getProducts) export default router;
额外优化建议
在getProduct控制器中提前校验参数是否为合法ObjectId,避免Mongoose抛出异常:
import mongoose from 'mongoose'; import Product from '../models/productM.js' export const getProduct = async (req, res) => { const productID = req.params.id // 校验ObjectId合法性 if(!mongoose.Types.ObjectId.isValid(productID)){ return res.status(400).json({message: '无效的产品ID格式'}) } try { const foundProduct = await Product.findById(productID) if(!foundProduct){ return res.status(404).json({message: '未找到该产品'}) } res.status(200).json(foundProduct) } catch (error) { res.status(500).json(error) } }
内容的提问来源于stack exchange,提问作者Mohammed Nacer Eddine Meddour
相关产品推荐
相关产品推荐

