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

React全栈项目编辑咖啡出现CastError并自动登出求助

问题描述

数据库连接正常,可完成认证登录并渲染存储的咖啡列表,但点击编辑某条咖啡时出现如下错误:

const castError = new CastError();
[1]                     ^
[1] 
[1] CastError: Cast to ObjectId failed for value ":id" (type string) at path "_id" for model "Coffee"

触发错误后会自动登出,数据未保存且数据库连接中断。


后端代码

coffeeRoutes.ts

const router = Router();

router.get('/', getAllCoffee);
router.get('/:id', getOneCoffee);
router.post('/', addCoffee);
router.put('/:id', editCoffee);
router.delete('/:id', deleteCoffee);

export default router;

coffee.ts

import { Document, Schema, Model, model } from 'mongoose';

interface ICoffee extends Document {
    name: string;
    description: string;
    price: number;
}

const coffeeSchema: Schema = new Schema({
    name: {
        type: String,
        required: true,
        unique: true
    },
    description: {
        type: String,
        required: true
    },
    price: {
        type: Number,
        required: true
    }
});

const Coffee: Model<ICoffee> = model<ICoffee>('Coffee', coffeeSchema);

export { ICoffee, Coffee };

coffeeController.ts

export const getAllCoffee: RequestHandler = async (req, res, next) => {
    let coffeeList = await Coffee.find();
    res.status(200).json(coffeeList);
}

export const getOneCoffee: RequestHandler = async (req, res, next) => {
    let itemId = req.params.id;
    let coffee = await Coffee.findById(itemId);
    res.status(200).json(coffee);
}

export const addCoffee: RequestHandler = async (req, res, next) => {
    let user: IUser | null = await verifyUser(req);

    if (!user) {
        return res.status(403).send();
    }

    const newCoffee: ICoffee = new Coffee({
        name: req.body.name,
        description: req.body.description,
        price: req.body.price
    });

    try {
        await newCoffee.save();
        res.status(201).json(newCoffee);
    }
    catch (err) {
        res.status(500).send(err);
    }
}

export const editCoffee: RequestHandler = async (req, res, next) => {
    let user: IUser | null = await verifyUser(req);

    if (!user) {
        return res.status(403).send();
    }

    let itemId = req.params.id;
    const updatedCoffee: ICoffee = new Coffee({
        _id: itemId,
        name: req.body.name,
        description: req.body.description,
        price: req.body.price
    });

    await Coffee.findByIdAndUpdate(itemId, { $set: updatedCoffee })

    res.status(200).json(updatedCoffee);
}

export const deleteCoffee: RequestHandler = async (req, res, next) => {
    let user: IUser | null = await verifyUser(req);

    if (!user) {
        return res.status(403).send();
    }

    let itemId = req.params.id;
    let result = await Coffee.findByIdAndDelete(itemId);
    res.status(200).json(result);
}

App.ts

const connectionString: string = 'mongodb://localhost:27017/testDB'
mongoose.connect(connectionString).then(
    () => console.log('database connection successful!'), 
    err => console.log('Error connecting to the database', err));

const app = express();

app.use(morgan('dev'));

app.use(express.json());
app.use(express.urlencoded({extended: true}));

const cors = require('cors');
const corsOptions = {
    origin: [ 'http://localhost:3001' ]
};
app.use(cors(corsOptions));

// routes
app.use('/api/coffee', coffeeRoutes);
app.use('/api/users', userRoutes);

app.use((req: Request, res: Response, next: NextFunction) => {
    res.status(404).end();
});

app.listen(3000);

终端报错信息

[1] OPTIONS /api/coffee/:id 204 0.696 ms - 0
[1] {
[1]   host: 'localhost:3000',
[1]   connection: 'keep-alive',
[1]   'content-length': '424',
[1]   accept: 'application/json, text/plain, */*',
[1]   authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2MzE2YzBiNjQyMjYyNDM1YzdiYWZhOTgiLCJpYXQiOjE2NjI1MjE5NjksImV4cCI6MTY2MjUyNTU2OX0.fAusx0ov8IjLA10YXZqL-OljrtShkUjMIA7SveC357k',
[1]   'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36',
[1]   'content-type': 'application/x-www-form-urlencoded',
[1]   'sec-gpc': '1',
[1]   origin: 'http://localhost:3001',
[1]   'sec-fetch-site': 'same-site',
[1]   'sec-fetch-mode': 'cors',
[1]   'sec-fetch-dest': 'empty',
[1]   referer: 'http://localhost:3001/',
[1]   'accept-encoding': 'gzip, deflate, br',
[1]   'accept-language': 'en-US,en;q=0.9'
[1] }
[1] /home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/query.js:4884
[1]   const castError = new CastError();
[1]                     ^
[1] 
[1] CastError: Cast to ObjectId failed for value ":id" (type string) at path "_id" for model "Coffee"
[1]     at model.Query.exec (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/query.js:4884:21)
[1]     at Query.then (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/query.js:4983:15)
[1]     at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
[1]   messageFormat: undefined,
[1]   stringValue: '":id"',
[1]   kind: 'ObjectId',
[1]   value: ':id',
[1]   path: '_id',
[1]   reason: BSONTypeError: Argument passed in must be a string of 12 bytes or a string of 24 hex characters or an integer
[1]       at new BSONTypeError (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/bson/lib/error.js:41:28)
[1]       at new ObjectId (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/bson/lib/objectid.js:67:23)
[1]       at castObjectId (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/cast/objectid.js:25:12)
[1]       at ObjectId.cast (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/schema/objectid.js:246:12)
[1]       at SchemaType.applySetters (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/schematype.js:1201:12)
[1]       at SchemaType._castForQuery (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/schematype.js:1648:15)
[1]       at SchemaType.castForQuery (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/schematype.js:1636:15)
[1]       at SchemaType.castForQueryWrapper (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/schematype.js:1612:20)
[1]       at cast (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/cast.js:347:32)
[1]       at Query.cast (/home/fgg/Desktop/backend-ts/l13-frontend/coffee-api/node_modules/mongoose/lib/query.js:5312:12),
[1]   valueType: 'string'
[1] }

前端代码

EditCoffee组件

import React, { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import axios from 'axios';

export const EditCoffee = () => {
    const [coffee, setCoffee] = useState({
        name: "",
        description: "",
        price: ""
    });

    let params = useParams();

    // let { editCoffee } = useContext(CoffeeProvider);

    function editCoffee(coffee, id) {
        let token = localStorage.getItem('myCoffeeToken')
        let headers = {
            Authorization: 'Bearer '  + token
        };
        return axios.put(baseUrl + id, coffee, { headers })
            .then(response => {
                getAllCoffee();
                return new Promise(resolve => resolve(response.data));
            }
        );
    }

    function getAllCoffee() {
        return axios.get(baseUrl).then(response => setCoffee(response.data))
    };

    const baseUrl = "http://localhost:3000/api/coffee/";

    let navigate = useNavigate();

    function handleChange(event) {
        setCoffee((prevValue) => {
            return { ...prevValue, [event.target.name]: event.target.value }
        });
    }

    function handleSubmit(event) {
        event.preventDefault();
        editCoffee(editCoffee, params.id).then(() => {
            navigate(`/coffee`);
        }).catch(error => {
            console.log(error);
            navigate(`/signin`);
        });
    }

    return (
        <form onSubmit={handleSubmit}>
            <h1>EDIT COFFEE</h1>
            <span>Coffee Name  </span>
            <input placeholder="Enter coffee name" type="text" name="name" value={coffee.name} onChange={handleChange} />
            <br></br><br></br>
            <span>Description  </span>
            <input placeholder="Enter description" type="text" name="description" value={coffee.description} onChange={handleChange} />
            <br></br><br></br>
            <span>Price  </span>
            <input placeholder="Enter price" type="number" name="price" value={coffee.price} onChange={handleChange} />
            <br></br><br></br>
            <button type='submit'>Edit Coffee</button>
        </form>
    )
};

CoffeeList组件

import React from 'react';
import CoffeeContext from '../contexts/CoffeeContext';
import { Link } from "react-router-dom";


function CoffeeList(props) {

    return (
        <CoffeeContext.Consumer>
        {
            ({ coffee }) => {
                return <div>
                    <h1>Coffee List</h1>
                    <Link to="/coffee/new">Add New Coffee</Link>
                    <div>
                        {coffee.map((c) => {
                            return (
                                <div key={c.id}>
                                    <h2>{c.name} | ${c.price}</h2>
                                    <p>{c.description}</p>
                                    <Link to={`/edit/${c.id}`}>
                                    <button>Edit</button>
                                    </Link>
                                    {/* <a href="/edit/id"><button>Edit</button></a> */}
                                    <button>Delete</button>
                                </div>
                            )
                        })}
                    </div>
                </div>
            }
        }
        </CoffeeContext.Consumer>
    );
}


export default CoffeeList;

问题修复方案

1. 核心修复:前端请求参数错误

从终端日志可见,请求URL为/api/coffee/:id,说明前端把路由占位符:id当成了实际ID值,未替换为咖啡的真实ID:

  • EditCoffee组件handleSubmit函数:调用editCoffee时传参错误,将函数本身传入,应改为传入状态中的coffee对象:
    // 错误代码
    editCoffee(editCoffee, params.id)
    // 修复后
    editCoffee(coffee, params.id)
    
  • 确认React Router路由配置:确保Edit页面的路由定义为path="/edit/:id",保证useParams()能正确获取ID。

2. 修复CoffeeList组件的ID字段

Mongoose文档的ID字段是_id而非id,遍历列表时需修正:

// 错误代码
<div key={c.id}>
<Link to={`/edit/${c.id}`}>
// 修复后
<div key={c._id}>
<Link to={`/edit/${c._id}`}>

3. 后端editCoffee控制器优化

  • 无需创建新Coffee实例,直接传入更新数据即可,同时添加错误捕获避免连接中断:
    export const editCoffee: RequestHandler = async (req, res, next) => {
        let user: IUser | null = await verifyUser(req);
    
        if (!user) {
            return res.status(403).send();
        }
    
        try {
            const updateData = {
                name: req.body.name,
                description: req.body.description,
                price: req.body.price
            };
            // { new: true } 返回更新后的文档
            const updatedCoffee = await Coffee.findByIdAndUpdate(req.params.id, updateData, { new: true });
            res.status(200).json(updatedCoffee);
        } catch (err) {
            res.status(400).json({ error: err.message });
        }
    }
    

4. 自动登出问题修复

自动登出是因为请求失败触发了catch块的navigate('/signin'),修复上述请求错误后该问题会自动解决。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 00:10:27