React注册表单提交出现500内部服务器错误及JSON解析异常求助
注册表单提交时出现500内部服务器错误及JSON解析错误
问题现象
提交React注册表单时触发两个错误:
POST http://localhost/auth/register 500 (Internal Server Error)
SubmitRegistration error: SyntaxError: Unexpected token '<', "JSON
表单支持用户名、邮箱、密码提交及头像上传。
相关代码
表单组件代码
<form onSubmit={(evt) => { RegistrationForm = document.getElementById('RegistrationForm'); NameInput = document.querySelector('#nameInput') EmailInput = document.querySelector('#emailInput') PasswordInput = document.querySelector('#passwordInput') ConfirmInput = document.querySelector('#confirm_passwordInput') ProfileInput = document.querySelector("#profile_picInput") var FormElements = { RegistrationForm, NameInput, EmailInput, PasswordInput, ConfirmInput, } HandleSignUpSubmit(evt, FormElements, profileImage, registerURL, dispatchFunctions, resetErrorFields) }} id="RegistrationForm" encType="multipart/form-data" className={`bg-[#f2e798] w-11/12 md:w-9/12 mx-auto lg:w-6/12 mt-[20px] py-10`}> <div id="Shell" className={`w-11/12 mx-auto ${FormStyle}`} > <div> <label htmlFor="username">Username</label> <input name="username" id="nameInput" type="text" placeholder="Type your username here" required className="" value={name} onChange={(evt) => onChangeHandler(evt, setName, resetErrorFields)} /> <div id="usernameError" className="ErrorDiv"> {usernameError != null && usernameError.length > 0 && RenderError(usernameError)} </div> </div> <div> <label htmlFor="email">Email</label> <input name="email" id="emailInput" type="text" placeholder="Type your email here" required className="" value={email} onChange={(evt) => onChangeHandler(evt, setEmail, resetErrorFields)} /> <div id="emailError" className="ErrorDiv"> {emailError != null && emailError.length > 0 && RenderError(emailError)} </div> </div> <div> <label htmlFor="profile_pic">Profile picture</label> <input name="profile_pic" id="profile_picInput" type="file" placeholder="Upload an image htmlFor your your profile picture here" className="file:rounded-lg file:font-['DecoTech'] file:bg-[#99cbae] file:text-white cursor-pointer" onChange={(evt) => { HandleFileChange(evt, setImage) }} /> </div> <div> <label htmlFor="password">password</label> <input name="password" id="passwordInput" type="password" placeholder="Type your password here" required className="" value={password} onChange={(evt) => onChangeHandler(evt, setPassword, resetErrorFields)} /> <div id="passwordError" className="ErrorDiv"> {passwordError != null && passwordError.length > 0 && RenderError(passwordError)} </div> </div> <div> <label htmlFor="confirm_password">Confirm your password</label> <input name="confirm_password" id="confirm_passwordInput" type="password" placeholder="Re-type your password here to confirm" required className="" value={confirmPass} onChange={(evt) => onChangeHandler(evt, setConfirmPass, resetErrorFields)} /> <div id="confirmError" className="ErrorDiv"> {confirmError != null && confirmError.length > 0 && RenderError(confirmError)} </div> </div> <div id="ButtonWrapper" className="grid sm:flex" > <button type="submit" className={SubmitStyle} value="Submit" >Submit</button> <button type="button" className={CancelStyle} value="Cancel" onClick={useCallback(() => navigate(-1))} >Cancel</button> </div> <hr className="my-[25px] h-[2px] border-[2px]" /> <h2 className="my-10 text-center text-lg">Already have an account with us?</h2> <button type="button" className="block rounded-full p-[10px] active:translate-x-[5px] active:translate-y-[5px] cursor-pointer border-white border-2 text-center w-[150px] select-none text-black mx-auto hover:bg-[#d48518] bg-[#e48f1a] text-center" onClick={useCallback(() => GoSignIn(navigate), [navigate])} >Log In</button> </div> </form>
文件上传处理函数
function HandleFileChange(evt, setImage) { const file = evt.target.files[0]; const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { setImage(reader.result); } }
Fetch提交函数
//apiURL = "http://localhost/auth/register" async function SubmitRegistration(data, apiURL, dispatchFunctions) { console.log("Registrating user") const { username, email, password, confirm_password, profile_pic, } = data; console.log("profile_pic: ", profile_pic) const formData = new FormData; formData.append("username", username); formData.append("email", email); formData.append("password", password); formData.append("confirm_password", confirm_password); formData.append("profile_pic", profile_pic); for (var key of formData.entries()) { console.log(key[0] + ', ' + key[1]) } const { GoHome, toggleDisplayAccountLink, setNewUser, } = dispatchFunctions; await fetch(apiURL, { method: "POST", headers: { "Content-Type": "multipart/form-data" }, body: formData }) .then(async response => { if (response.ok) { console.log("Registration is successful.") await response.json() .then(data => { localStorage.setItem("user", JSON.stringify(data.user)) localStorage.setItem('token', data.token) //setNewUser and toggleDisplayAccoutLink updates the header bar to contain //data about the logged in user setNewUser(data.user) toggleDisplayAccountLink(true), GoHome(); }) } else { const result = await response.json() console.log("Registration failed with status code: ", result.error) RenderErrorArray(result.error, dispatchFunctions) } }) .catch(error => { console.log("SubmitRegistration error: ", error) }) }
服务端路由配置
const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "public/uploads") }, filename: function (req, file, cb) { const ext = path.extname(file.originalname); const filename = `${Date.now()}-${file.filename}${ext}`; cb(null, filename) } }); const upload = multer({ storage: storage }); router.post('/register', cors(), upload.single('profile_pic'), authController.Register)
服务端控制器代码
exports.Register = [ body("username") .trim() .isLength({ min: 1 }) .withMessage("You must type your username.") .escape(), body("email") .trim() .isLength({ min: 1 }) .withMessage("You must type your email address. ") .custom((value) => { if (!checkEmail(value)) { throw new Error("The email format is not valid. It must be something along the lines of Bob@email.com."); } return true; }), body("password") .trim() .isLength({ min: 4 }) .withMessage("Your password must be at least 4 characters long."), body("profile_pic"), body("confirm_password") .trim() .isLength({ min: 4 }) .withMessage("Your confirmation password must be at least 4 characters long.") .custom((value, { req }) => { if (value != req.body.password) { throw new Error("Your password and confirmation password should match.") } //this is necessary to indicate that the validation passed return true }), async (req, res, next) => { const { username, email, } = req.body; const errors = validationResult(req); const UserList = await User.find({}) UserList.forEach(val => { if (val.username == username.trim()) { const obj = { param: "username", msg: "That username already exists in our database." } errors.errors.push(obj) } if (val.email.toLowerCase() == email.trim().toLowerCase()) { const obj = { param: "email", msg: "That email already exists in our database." } errors.errors.push(obj) } }) if (!errors.isEmpty()) { return res.status(400).json({ error: errors.array() }); } try { const hashedPassword = await bcrypt.hash(req.body.password, 10) const obj = { username: username.replace(/\s/g, ''), email: email, password: hashedPassword, joinedDate: Date.now(), } if (req.files && req.files.profile_pic) { obj.profile_pic = req.files.profile_pic[0].buffer } const newUser = new User(obj); const result = await newUser.save(); const savedUser = { username: result.username, email: result.email, joinedDate: result.joinedDate, profile_pic: result.profile_pic, id: result._id, } console.log("User is successfully created.") const token = jwt.sign(savedUser.toJSON(), process.env.JWT_SECRET, { expiresIn: 60 * 60 }) return res.status(200).json({ user: savedUser, token, message: "User is successfully saved in the database" }); } catch (e) { console.log("Error in trying to create new user: ", e.message) res.status(500).json({ error: e.message }); } } ]
问题排查及解决方案
1. 客户端文件上传错误
HandleFileChange里把文件转成了Data URL(base64字符串),但服务端用multer接收的是原始文件对象,不是base64字符串,导致服务端无法解析文件抛出错误。
修复:直接保存文件对象而非base64:
function HandleFileChange(evt, setImage) { const file = evt.target.files[0]; setImage(file); }
2. Fetch请求头错误
手动设置Content-Type: multipart/form-data会导致浏览器无法自动生成正确的boundary分隔符,服务端无法解析FormData。
修复:删除Fetch的headers配置,让浏览器自动处理:
await fetch(apiURL, { method: "POST", body: formData })
3. 服务端文件读取错误
使用upload.single('profile_pic')时,文件存在req.file而非req.files.profile_pic;同时multer文件名生成逻辑错误,file.filename应为file.originalname。
修复:
- 修正multer文件名生成:
filename: function (req, file, cb) { const ext = path.extname(file.originalname); const filename = `${Date.now()}-${file.originalname.replace(ext, '')}${ext}`; cb(null, filename) }
- 修正控制器文件获取逻辑:
if (req.file) { obj.profile_pic = req.file.path; // 建议存文件路径,而非buffer到数据库 }
4. 服务端用户查询性能问题
User.find({})遍历所有用户检查重复,性能极差,改用数据库直接查询:
const existingUser = await User.findOne({ $or: [ { username: username.trim() }, { email: email.trim().toLowerCase() } ] }); if (existingUser) { if (existingUser.username === username.trim()) { errors.errors.push({ param: "username", msg: "That username already exists in our database." }); } if (existingUser.email.toLowerCase() === email.trim().toLowerCase()) { errors.errors.push({ param: "email", msg: "That email already exists in our database." }); } }
5. JSON解析错误原因
客户端的SyntaxError是因为服务端返回了HTML错误页而非JSON,修复上述问题后,确保服务端所有错误都返回JSON格式即可解决。
内容的提问来源于stack exchange,提问作者antsmasher
相关产品推荐
相关产品推荐

