如何在React中通过Fetch渲染Node.js返回的错误信息
问题
登录页面在用户输入正确凭证时可正常工作,但测试错误用户名或密码时,前端catch块无法正确解析后端返回的错误对象,导致错误信息无法渲染。
尝试过两种后端响应方式均存在问题:
- 使用
res.send(401).json({"message":"Unauthorized"});:不会触发错误响应,被当作正常返回处理 - 使用
res.sendStatus(401);:能触发错误,但前端catch块无法处理响应内容,控制台报错SyntaxError: Unexpected token 'U', "Unauthorized" is not valid JSON
后端代码:
const User = require('../model/User'); const jwt = require('jsonwebtoken'); const bcrypt = require('bcrypt'); const handleLogin = async (req,res) => { const user = req.body.user.toLowerCase(); const pwd = req.body.pwd; if(!user || !pwd) return res.sendStatus(400); const foundUser = await User.findOne({username: user}).exec(); if(!foundUser) return res.sendStatus(401); const match = await bcrypt.compare(pwd, foundUser.password); console.log(match); if(match){ const roles = Object.values(foundUser.roles); const accessToken = jwt.sign( {"userInfo": { "username": foundUser.username, "roles": roles }}, process.env.ACCESS_TOKEN, {expiresIn: "300s"} ); const refreshToken = jwt.sign( {"username": foundUser.username}, process.env.REFRESH_TOKEN, {expiresIn: "1d"} ); foundUser.refreshToken = refreshToken; const result = await foundUser.save(); if(!result) return res.status(500); res.cookie("jwt",refreshToken,{httpOnly: true, sameSite: "None", maxAge: 24*60*60*1000}); res.json({user, roles, accessToken}); } else{ res.sendStatus(401); } } module.exports = {handleLogin};
前端Fetch代码:
fetch(BASE_URL + "/login", { method: "POST", headers: { "Content-Type":"application/json" }, body: JSON.stringify({user: username,pwd}) }) .then(res => res.json()) .then(data => { setUser(data); console.log(data); }) .then(() => { setSuccess(true); setTimeout(() => { navigate("/"); }, 1000); }) .catch((err)=>{ console.log(err); if(err.status == "401"){ setErrMsg("Wrong username or password.") } else{ setErrMsg("Login failed, try again.") } errRef.current.focus(); })
解决方案
后端调整
核心问题:res.sendStatus(401)返回纯文本"Unauthorized",前端用res.json()解析会触发语法错误;而res.send(401).json(...)写法错误,send(401)已发送响应,后续json()不会生效。
正确做法是用res.status(状态码).json(错误信息),同时设置状态码和JSON格式响应:
const handleLogin = async (req,res) => { const user = req.body.user.toLowerCase(); const pwd = req.body.pwd; if(!user || !pwd) return res.status(400).json({message: "Username and password are required"}); const foundUser = await User.findOne({username: user}).exec(); if(!foundUser) return res.status(401).json({message: "Unauthorized"}); const match = await bcrypt.compare(pwd, foundUser.password); console.log(match); if(match){ const roles = Object.values(foundUser.roles); const accessToken = jwt.sign( {"userInfo": { "username": foundUser.username, "roles": roles }}, process.env.ACCESS_TOKEN, {expiresIn: "300s"} ); const refreshToken = jwt.sign( {"username": foundUser.username}, process.env.REFRESH_TOKEN, {expiresIn: "1d"} ); foundUser.refreshToken = refreshToken; const result = await foundUser.save(); if(!result) return res.status(500).json({message: "Server error"}); res.cookie("jwt",refreshToken,{httpOnly: true, sameSite: "None", maxAge: 24*60*60*1000}); res.json({user, roles, accessToken}); } else{ res.status(401).json({message: "Unauthorized"}); } }
前端调整
Fetch API的catch块仅捕获网络错误,HTTP错误状态码(如401、400)不会触发catch,必须先判断响应状态是否正常:
fetch(BASE_URL + "/login", { method: "POST", headers: { "Content-Type":"application/json" }, body: JSON.stringify({user: username,pwd}) }) .then(res => { // 先判断响应状态是否在200-299范围内 if (!res.ok) { // 解析错误响应的JSON内容,抛出包含错误信息的异常 return res.json().then(errData => { throw new Error(errData.message); }); } return res.json(); }) .then(data => { setUser(data); console.log(data); setSuccess(true); setTimeout(() => { navigate("/"); }, 1000); }) .catch((err)=>{ console.log(err); // 根据错误信息匹配对应的提示 if(err.message === "Unauthorized"){ setErrMsg("Wrong username or password.") } else if(err.message === "Username and password are required"){ setErrMsg("Please enter username and password.") } else { setErrMsg("Login failed, try again.") } errRef.current.focus(); })
内容的提问来源于stack exchange,提问作者newnoobX
相关产品推荐
相关产品推荐

