You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 05:25:14