注册时服务器崩溃报错Can't set headers after they are sent,但数据已存MongoDB
解决注册时出现的
Error: Can't set headers after they are sent问题 嘿,这个问题太典型了!你遇到的这个错误,核心原因是一个HTTP请求只能对应一次响应,而你的代码直接违反了这个规则。
看你user.save()的回调函数部分:
user.save(function (err) { if (err) { return next(err) } res.json({ user_id: user._id, token: tokenForUser(user) }); res.json({ data: { email: email, password: password, userData: [{ role: role, firstName: firstName, lastName: lastName, phone: phone, address: [{ number: number, street: street, city: city, postcode: postcode }], }], } }); });
你连续调用了两次res.json()——第一次调用时,服务器已经把响应头和内容发送给客户端了,此时这个请求的响应流程已经结束。当你第二次调用res.json()时,服务器试图再次设置响应头,自然就触发了这个错误,甚至导致服务器崩溃。
至于数据能正常存入MongoDB,是因为user.save()已经成功完成了数据库写入操作,响应阶段的错误并不会回滚已经完成的数据库操作。
修正后的代码
你只需要把要返回的内容合并到一个res.json()里就行,比如:
exports.signup = function (req, res, next) { var email = req.body.email; var password = req.body.password; var role = req.body.role; var firstName = req.body.firstName; var lastName = req.body.lastName; var phone = req.body.phone; var number = req.body.number; var street = req.body.street; var city = req.body.city; var postcode = req.body.postcode; if (!email || !password) { return res.status(422).json({ error: "You must provide an email and password" }); } // Check if user already exists, send error if they do User.findOne({ email: email }, function (err, existingUser) { if (err) { return next(err) } if (existingUser) { return res.status(422).json({ error: "Email taken" }) } var user = new User({ email: email, password: password, userData: [{ role: role, firstName: firstName, lastName: lastName, phone: phone, address: [{ number: number, street: street, city: city, postcode: postcode }], }], }); user.save(function (err) { if (err) { return next(err) } // 合并所有需要返回的内容到一个响应中 res.json({ user_id: user._id, token: tokenForUser(user), data: { email: email, // 注意:绝对不要返回明文密码!这里只是示例,实际要删除这一行 // password: password, userData: [{ role: role, firstName: firstName, lastName: lastName, phone: phone, address: [{ number: number, street: street, city: city, postcode: postcode }], }] } }); }); }); }
额外的重要建议
- 禁止存储明文密码:现在你的代码直接把明文密码存入数据库,这是严重的安全漏洞。一定要用bcrypt这类加密库对密码进行哈希后再存储,验证密码时再比对哈希值。
- 替换回调为async/await:回调嵌套会让代码变得难以维护,用
async/await语法可以让逻辑更清晰,比如:exports.signup = async function (req, res, next) { try { // 提取请求参数 const { email, password, role, firstName, lastName, phone, number, street, city, postcode } = req.body; // 基础验证 if (!email || !password) { return res.status(422).json({ error: "You must provide an email and password" }); } // 检查用户是否存在 const existingUser = await User.findOne({ email }); if (existingUser) { return res.status(422).json({ error: "Email taken" }); } // 创建并保存用户 const user = new User({ email, password, userData: [{ role, firstName, lastName, phone, address: [{ number, street, city, postcode }] }] }); await user.save(); // 返回响应 res.json({ user_id: user._id, token: tokenForUser(user), data: { email, userData: user.userData } }); } catch (err) { next(err); } } - 完善输入验证:可以添加邮箱格式校验、密码强度校验、必填字段检查(比如姓名、电话等),让接口更健壮。
内容的提问来源于stack exchange,提问作者Markus Hayner
相关产品推荐
相关产品推荐

