使用Mongoose更新数据时为何出现空字符串?
解决用户信息更新时未修改字段被置空的问题
我想更新用户的特定信息、保留其他原有数据,但现在碰到个问题:只要不填写某个字段,提交后该字段就会变成空字符串。比如只修改status字段时,返回结果里的studentId就变成了空:
"studentId": "",
管理员本该能单独修改studentId或status这类字段,但现在改其中一个时,没碰的字段都会被置空。
前端React代码
const [data,setData] = useState({ studentId: '', status: '' }) const handleClick = async (e) => { e.preventDefault() try { await userRequest.put(`/users/${userId}`,data) } catch (error) { console.log({error: error.message}) } } <div className="formInput"> <label>Student ID</label> <input type="text" placeholder={userInfo.studentId} onChange={(e) => setData({...data, studentId: e.target.value})} /> </div> <div className="formInput"> <FormControl sx={{ m: 1, minWidth: 200 }}> <InputLabel id="demo-simple-select-helper-label">Status</InputLabel> <Select labelId="demo-simple-select-helper-label" id="demo-simple-select-helper" defaultValue={userInfo.status} label="Status" onChange={(e) => setData({...data, status: e.target.value})} > <MenuItem value=""> <em>None</em> </MenuItem> <MenuItem value="pending">Pending</MenuItem> <MenuItem value="active">Active</MenuItem> <MenuItem value="disable">Disable</MenuItem> </Select> </FormControl> </div>
后端Backend.js代码
router.put('/:id', async (req,res) =>{ if(req.body.password){ req.body.password = CryptoJS.AES.encrypt( req.body.password, process.env.PASSWORD_SEC) .toString(); } try { const updatedUser = await User.findByIdAndUpdate(req.params.id, { $set: req.body }, {new: true}) res.status(200).json(updatedUser) console.log(updatedUser) } catch (error) { res.status(500).json({message:error.message}) } })
解决办法
前端修复(核心)
问题根源在前端状态初始化——你一开始把data里的字段都设成了空字符串,提交时这些空值会被一起发给后端,直接覆盖数据库里的原有数据。正确的做法是用用户的现有信息初始化状态:
// 用userInfo的现有值初始化状态,没有的话才设为空 const [data, setData] = useState({ studentId: userInfo.studentId || '', status: userInfo.status || '' })
另外,输入框要绑定状态的value属性,而不是只靠placeholder显示原有值,这样用户不修改时,状态里保留的就是原有信息:
<div className="formInput"> <label>Student ID</label> {/* 绑定value,确保输入框显示现有值,提交时保留原值 */} <input type="text" value={data.studentId} onChange={(e) => setData({...data, studentId: e.target.value})} /> </div>
Select组件同样把defaultValue换成value绑定状态,保证选中状态和数据一致:
<Select labelId="demo-simple-select-helper-label" id="demo-simple-select-helper" value={data.status} // 绑定状态值 label="Status" onChange={(e) => setData({...data, status: e.target.value})} >
这样用户只修改需要改的字段,未修改的字段会带着原有值提交,就不会被置空了。
后端优化(可选,增加安全性)
为了防止前端不小心传空值,后端可以过滤掉请求体里的空字段,只更新有有效值的部分:
router.put('/:id', async (req, res) => { if (req.body.password) { req.body.password = CryptoJS.AES.encrypt( req.body.password, process.env.PASSWORD_SEC ).toString(); } // 过滤掉空字符串、null、undefined的字段 const updateData = Object.fromEntries( Object.entries(req.body).filter(([key, value]) => value !== '' && value != null) ); try { const updatedUser = await User.findByIdAndUpdate( req.params.id, { $set: updateData }, // 只用过滤后的非空数据更新 { new: true } ); res.status(200).json(updatedUser); console.log(updatedUser); } catch (error) { res.status(500).json({ message: error.message }); } });
这样就算前端出问题传了空值,后端也会忽略这些字段,不会覆盖数据库里的原有数据。
内容的提问来源于stack exchange,提问作者Stykgwar
相关产品推荐
相关产品推荐

