Next.js+React调用API删除MongoDB文档不生效问题
MongoDB单条文档删除功能失效修复
问题说明
核心需求为从MongoDB集合删除单条文档,现有实现逻辑为:index页面遍历渲染数据库查询返回的条目列表,将每个条目对象传入DeleteTest删除组件;组件内放置删除按钮,点击触发deleteTestFun函数调用后端删除API,运行时API始终无法正常完成删除操作。
现有代码
index页面代码
import React, { useState } from 'react'; import AppBar from '@mui/material/AppBar'; import Button from '@mui/material/Button'; import Card from '@mui/material/Card'; import CardActions from '@mui/material/CardActions'; import CardContent from '@mui/material/CardContent'; import CardMedia from '@mui/material/CardMedia'; import CssBaseline from '@mui/material/CssBaseline'; import Grid from '@mui/material/Grid'; import Stack from '@mui/material/Stack'; import Box from '@mui/material/Box'; import Toolbar from '@mui/material/Toolbar'; import Typography from '@mui/material/Typography'; import Container from '@mui/material/Container'; import Link from '@mui/material/Link'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import Form from '../components/form'; import DeleteIcon from '@mui/icons-material/Delete'; import connectMongo from '../utlis/connectMongo'; import Test from '../models/testModel'; import Checkbox from '@mui/material/Checkbox'; import DeleteTest from '../components/deleteTest'; const theme = createTheme(); export default function Album({ tests }) { return ( <div> <Form></Form> <div> {tests.map((test) => ( <a key={test._id}> <p> {test.name} {test.email} </p> <DeleteTest test={test}></DeleteTest> </a> ))} </div> </div> ); } export const getServerSideProps = async () => { try { console.log('CONNECTING TO MONGO'); await connectMongo(); console.log('CONNECTED TO MONGO'); console.log('FETCHING DOCUMENTS'); const tests = await Test.find(); console.log('FETCHED DOCUMENTS'); return { props: { tests: JSON.parse(JSON.stringify(tests)), }, }; } catch (error) { console.log(error); return { notFound: true, }; } };
DeleteTest删除组件代码
import React, { useState } from 'react'; import Button from '@mui/material/Button'; import CardActions from '@mui/material/CardActions'; import CardContent from '@mui/material/CardContent'; import CardMedia from '@mui/material/CardMedia'; import CssBaseline from '@mui/material/CssBaseline'; import Stack from '@mui/material/Stack'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import DeleteIcon from '@mui/icons-material/Delete'; export default function DeleteTest(props) { const postID = props.test._id; const name = props.test.name; const deleteTestFun = async (postID) => { const response = await fetch('/api/delete/' + postID, { method: 'DELETE', }); const data = await response.json(); console.log(data); }; return ( <div> <Button onClick={deleteTestFun}>Delete</Button> </div> ); }
API路由初始版本代码
import connectMongo from '../../../utlis/connectMongo'; import Test from '../../../models/testModel'; import { MongoClient } from 'mongodb'; import mongoose from 'mongoose'; /** * * @param {import("next").NextApiRequest} req * @param {import("next").NextApiResponse} res */ export default async function deleteTest(req, res) { try { const object = Test.find({ _id: req.query.postID }); console.log(object.name); await Test.findOneAndDelete({ name: req.query.name }); res.send('Item deleted'); } catch (error) { console.log(error); } }
API路由更新版本代码
export default async function deleteTest(req, res) { try{ console.log(req.body.name); const test = await Test.remove(req.body) res.send('Item deleted'); }catch(error) { console.log(error) } }
问题根因
- 前端参数传递错误:
deleteTestFun绑定按钮点击事件时,默认接收的参数是点击事件对象,而非组件内定义的postID变量,导致请求路径拼接的ID实际是事件对象,后端无法获取正确的文档ID;初始版本API尝试读取req.query.name,但前端请求未传递name字段,查询条件完全不匹配。 - 后端缺少数据库连接步骤:两个版本的API都未执行
connectMongo初始化数据库连接,直接调用Model方法无法执行数据库操作。 - 数据库操作逻辑错误:初始版本中
Test.find()返回的是Mongoose Query对象,未加await获取查询结果就直接读取object.name会直接报错;使用name字段作为删除条件,存在重名时会误删数据,必须使用文档唯一标识_id作为删除条件。 - Mongoose方法使用错误:更新版本使用的
Test.remove()方法已在新版Mongoose中废弃,且未传入正确的匹配条件,也未将前端传来的字符串ID转换为MongoDB可识别的ObjectId格式,无法匹配到对应文档。
修复方案
1. 修复DeleteTest组件
移除deleteTestFun的形参,直接使用闭包内提前获取的postID发起请求,删除成功后可刷新页面拉取最新列表:
export default function DeleteTest({ test }) { const postID = test._id; const deleteTestFun = async () => { const response = await fetch(`/api/delete/${postID}`, { method: 'DELETE', }); const data = await response.json(); console.log(data); // 删除成功后刷新列表,可替换为状态更新实现无刷新效果 window.location.reload(); }; return ( <div> <Button onClick={deleteTestFun} startIcon={<DeleteIcon />}>Delete</Button> </div> ); }
2. 修复删除API路由
首先初始化数据库连接,从路径参数获取文档ID并转换为ObjectId格式,使用findByIdAndDelete方法按唯一ID删除,补充状态码返回和错误处理:
import connectMongo from '../../../utlis/connectMongo'; import Test from '../../../models/testModel'; import mongoose from 'mongoose'; /** * * @param {import("next").NextApiRequest} req * @param {import("next").NextApiResponse} res */ export default async function deleteTest(req, res) { try { // 初始化数据库连接 await connectMongo(); // 转换ID格式 const postId = new mongoose.Types.ObjectId(req.query.postID); // 按ID删除文档 const deleteResult = await Test.findByIdAndDelete(postId); if (!deleteResult) { return res.status(404).json({ msg: '待删除文档不存在' }); } res.status(200).json({ msg: '删除成功', data: deleteResult }); } catch (error) { console.log(error); res.status(500).json({ msg: '删除失败', error: error.message }); } }
配置注意事项
- 确认API路由文件命名符合Next.js动态路由规则,文件需命名为
[postID].js,存放在pages/api/delete/目录下,才能正确匹配/api/delete/xxx格式的请求,拿到req.query.postID参数。 - 若需要实现无刷新列表更新,可将条目列表的状态提升到index父组件,删除成功后通过回调函数过滤掉已删除的条目,无需整页刷新。
内容的提问来源于stack exchange,提问作者ClockMaker12
相关产品推荐
相关产品推荐

