Next.js使用变量提交数据至MongoDB失败,硬编码则正常
背景信息
正在学习新技术,开发Next.js前端项目,Express后端已通过Postman测试,所有API端点正常工作。使用MongoDB作为数据库,已配置cors。
问题现象
- 通过表单收集
employeeData发起POST请求时,控制台打印employeeData所有字段均存在且正确,但数据无法存入MongoDB,后端返回状态码200 - 使用硬编码的
formData发起相同POST请求时,数据可正常存入数据库
相关代码
表单提交的请求代码
try { const response = await fetch('http://localhost:8080/api/v1/employee', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(employeeData), }); if (response.ok) { console.log('Employee saved successfully'); } else { console.error('Error saving employee:', response.statusText); console.log('Response Body:', await response.json()); } } catch (error) { console.error('Error saving employee:', error); }
硬编码成功的请求代码
const formData = { name: 'John', surname: 'Doe', telephone: '0123456789', email: 'johndoe@gmail.com', manager: 'Manager A', status: true, }; try { const response = await fetch('http://localhost:8080/api/v1/employee', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData), }); if (response.ok) { console.log('Employee saved successfully'); } else { console.error('Error saving employee:', response.statusText); console.log('Response Body:', await response.json()); } } catch (error) { console.error('Error saving employee:', error); }
完整addEmployee.tsx代码
import React, { useState, useEffect } from 'react'; import Container from '@mui/material/Container'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import TextField from '@mui/material/TextField'; import Button from '@mui/material/Button'; import Select from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; import InputLabel from '@mui/material/InputLabel'; import FormControl from '@mui/material/FormControl'; import axios from 'axios'; import MenuBar from '@/components/MenuBar'; import { useRouter } from 'next/router'; export default function addEmployee() { const router = useRouter(); const [managerOptions, setManagerOptions] = useState([]); const [employeeData, setEmployeeData] = useState({ name: '', surname: '', telephone: '', email: '', manager: '', }); useEffect(() => { axios .get('http://localhost:8080/api/v1/employee') .then((response) => { console.log('Employee data:', response.data); setManagerOptions( response.data.map( (employee) => `${employee.name} ${employee.surname}` ) ); }) .catch((error) => { console.error('Error fetching employee data:', error); }); }, []); const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = event.target; setEmployeeData((prevData) => ({ ...prevData, [name]: value })); }; const handleManagerChange = (event: React.ChangeEvent<{ value: unknown }>) => { setEmployeeData((prevData) => ({ ...prevData, manager: event.target.value as string })); }; const handleButtonClick = async (option: string) => { switch (option) { case 'Save': if(employeeData.name === '' || employeeData.surname === '' || employeeData.telephone === '' || employeeData.email === '') { alert("Required fields are not filled in!"); break; }; try { const response = await fetch('http://localhost:8080/api/v1/employee', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(employeeData), }); if (response.ok) { console.log('Employee saved successfully'); console.log(response); } else { console.error('Error saving employee:', response.statusText); console.log('Response Body:', await response.json()); } } catch (error) { console.error('Error saving employee:', error); } router.push('/employee'); break; case 'Cancel': router.push('/employee'); break; default: break; } }; return ( <Container> <MenuBar /> <Container maxWidth="md"> <Box mt={4} mb={2}> <Typography variant="h5">Create Employee</Typography> </Box> <Box border={1} borderColor="grey.400" p={2} borderRadius={4}> <form> <Box mb={2}> <TextField label="Name*" fullWidth name="name" onChange={handleInputChange} /> </Box> <Box mb={2}> <TextField label="Surname*" fullWidth name="surname" onChange={handleInputChange} /> </Box> <Box mb={2}> <TextField label="Telephone Number*" fullWidth name="telephone" onChange={handleInputChange} /> </Box> <Box mb={2}> <TextField label="Email Address*" fullWidth name="email" onChange={handleInputChange} /> </Box> <Box mb={2}> <FormControl fullWidth> <InputLabel>Manager</InputLabel> <Select value={employeeData.manager} onChange={handleManagerChange} displayEmpty > {managerOptions.map(manager => ( <MenuItem key={manager} value={manager}> {manager} </MenuItem> ))} </Select> </FormControl> </Box> <Button type="button" variant="contained" color="inherit" onClick={() => handleButtonClick('Save')}> Save </Button> <Button variant="contained" color="inherit" onClick={() => handleButtonClick('Cancel')}> Cancel </Button> </form> </Box> </Container> </Container> ); }
问题分析与解决方案
1. 缺失status字段
对比硬编码的formData和表单的employeeData,发现硬编码数据包含status: true,但表单的employeeData初始状态没有status字段,提交时也不会携带该字段。若MongoDB的Employee Schema中status是必填字段(即使有默认值,前端未传可能导致后端处理异常),会出现数据无法存入但返回200的情况(后端可能未做严格校验,直接返回成功但数据库未写入)。
解决方案:
在employeeData的初始状态中添加status字段,默认设为true:
const [employeeData, setEmployeeData] = useState({ name: '', surname: '', telephone: '', email: '', manager: '', status: true, // 添加该字段 });
2. 路由跳转过早
当前代码中,fetch请求是异步操作,但在try/catch块之后立即执行router.push('/employee'),可能导致请求还未完成就跳转到其他页面,虽浏览器会继续完成请求,但如果后端处理依赖前端后续状态,或请求被意外中断,也可能导致数据未写入。
解决方案:
将router.push移到response.ok的判断分支内,确保请求成功后再跳转:
try { const response = await fetch('http://localhost:8080/api/v1/employee', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(employeeData), }); if (response.ok) { console.log('Employee saved successfully'); console.log(response); router.push('/employee'); // 移到此处 } else { console.error('Error saving employee:', response.statusText); console.log('Response Body:', await response.json()); } } catch (error) { console.error('Error saving employee:', error); } // 移除此处的router.push
3. 后端字段校验问题
如果后端Schema中某些字段有格式校验(比如telephone的格式、email的格式),表单输入的数据可能不符合校验规则,但后端返回了200(比如错误处理不当,未返回错误状态码)。
验证方法:
在控制台打印employeeData的具体值,和硬编码的formData对比,检查字段格式是否一致。比如telephone是否是字符串类型、email是否符合邮箱格式。
解决方案:
- 在前端添加字段格式校验,确保输入符合后端要求
- 检查后端错误处理逻辑,确保不符合校验的数据返回非200状态码,并返回错误信息
内容的提问来源于stack exchange,提问作者Jandrie Visagie

