try-catch 的 catch 里面务必输出 error.message,否则 try 内的代码如果报错,我们是找不到错误原因的!非常坑!
error.message 其实就是正常的浏览器输出的错误中的 message!
例子:
//captcha logic
onCaptchaShow(async (ticket, randstr) => {
try {
const res = await passwordLogin(formData.email, formData.password, ticket, randstr, roleLogin); //real logic
const newToken = res.data.token; //real logic
const role = res.data.role;
let getInfoFun = getUserInfo;
if (role === "member") {
getInfoFun = getUserInfo;
} else if (role === "trainer") {
getInfoFun = getTrainerInfo;
} else if (role === "admin") {
getInfoFun = getAdminInfo;
}
// let getInfoFun = getUserInfo; // test data
// const newToken = "123456"; // test data
await setToken(newToken, role, getInfoFun);
showSnackbar({ message: "Login Successful!", severity: "success" });
console.log("Login Successful!", role);
//base the role to redirect to the right page
// navigate(`/${role}`); // don't need this logic
} catch (error) {
if (error) {
//要做错误处理,不然 try-catch之后直接就没办法看到报错了,导致找不到问题所在 ——> 关键点
showSnackbar({ message: error.message || "Login failed. Please try again.", severity: "error" });
}
}
},
也就是说不能只写这个 "Login failed. Please try again." 这种固定内容的错误提示!不全面,很烂的代码!
一开始我们把 roleLogin 写成 role,造成变量的重复定义,但是由于被 catch 了,所以怎么都找不到错误原因!!!
前端错误处理的总结
接口的错误在公共 axios 里面处理
根据后端状态码(http 是正确的情况下,后端的自定义状态码),和后端给的错误信息 msg 进行输出
service.interceptors.response.use(
(response) => {
const res = response?.data;
//Normally, the exception code returned by the back end will still go here
if (res?.code !== 200) {
console.log("response:", res);
console.log("If code is not 200, an error is reported by default");
return Promise.reject({ response }); // Force the error handling logic
} else {
//Direct return response.data
return res;
}
},
(error) => {
// From code! ==200 logic jump over
const res = error?.response;
console.log(res);
// As long as you set code then the back end must set message
if (res?.data?.message && res?.data?.code) {
// error.message = res.data.message;
errorNotifier.showError(res.data.message);
return Promise.reject();
} else if (error?.message) {
//Otherwise, let the page handle it
console.log("error.message:", error?.message, error);
// errorNotifier.showError(error?.message);
return Promise.reject(error);
} else {
//Otherwise, let the page handle it
console.log("error:", error);
return Promise.reject(error);
}
}
);
页面内部的错误在 catch 里面处理(或者后端没给错误信息 msg)
try {
const res = await passwordLogin(
formData.email,
formData.password,
ticket,
randstr,
roleLogin
); //real logic
const newToken = res.data.token; //real logic
const role = res.data.role;
let getInfoFun = getUserInfo;
if (role === "member") {
getInfoFun = getUserInfo;
} else if (role === "trainer") {
getInfoFun = getTrainerInfo;
} else if (role === "admin") {
getInfoFun = getAdminInfo;
}
// let getInfoFun = getUserInfo; // test data
// const newToken = "123456"; // test data
await setToken(newToken, role, getInfoFun);
showSnackbar({ message: "Login Successful!", severity: "success" });
console.log("Login Successful!", role);
//base the role to redirect to the right page
// navigate(`/${role}`); // don't need this logic
} catch (error) {
if (error) {
//要做错误处理,不然 try-catch之后直接就没办法看到报错了,导致找不到问题所在 ——> 关键点
showSnackbar({
message: error.message || "Login failed. Please try again.",
severity: "error",
});
}
}
更正:之前的思路是错误的!
==因为我们实际上发现上面 return Promise.reject({ response });之后也走不到下面的 error 处理,下面的 error 只能处理真正的请求错误,而不是后端构造的错误信息(后端构造的统一视为正确返回),错误的返回其实只存在 axios 错误一种可能(比如服务器连接不上,没有网络等)==
所以统一的错误处理必须要在上面!
下面才是正确的统一的大一统写法:但不是最终版,我们还要仔细分析一下
service.interceptors.response.use(
(response) => {
const res = response?.data;
if (res?.code !== 200) {
console.log("response:", res, { response });
console.log("If code is not 200, an error is reported by default");
// return Promise.reject({ response }); //这样行不通,走不到下面那里
// return Promise.reject(new Error(res.message || "Error")); //这样不利于我们公共弹窗报错
// 后端只要设置了code 就应该一定设置了 message
if (res?.message && res?.code) {
errorNotifier.showError(res.message);
return Promise.reject(); //这个地方必须要返回,否则默认会返回 resolve 的,页面 try-catch会不起作用,不知道代码出了错误!会导致页面中代码逻辑意外向下执行!//而且页面必须写 try-catch,否则页面会直接产生意外报错,不太好
} else {
//如果没设置 message,那就让页面处理
console.log("error:", res);
return Promise.reject(new Error(""));
}
} else {
//Direct return response.data
return res;
}
},
(error) => {
// 这里只处理 axios 的 error
// errorNotifier.showError(error?.message || "Error"); //但是这种错误最好是让页面自己处理(因为我们不知道每个页面错误的时候应该说什么具体的东西,比如登录页面要说:登录错误请重试!)
return Promise.reject(new Error(error?.message));
}
);
这是 axios 错误的示例:
页面里面处理错误:
1.正常情况下,如果后端确保每个接口都在错误情况的时候正确返回了错误的 msg 和 code 了,那么其实我们页面中没有必要做任何处理!
因为页面上报错的唯一可能就是我们的页面 js 代码报错了,而请求的错误都在 axios 封装里面集中处理完了!
所以页面根本没必要进行任何的错误处理,但是 try-catch 还是必须要写的!不然代码走不下去!
const handleGetStatisticData = async () => {
if (!selectionRange.startDate || !selectionRange.endDate) return;
setLoading(true);
try {
await getData();
} catch (error) {
// console.log(error); //这里一定是 undefined
}
// await getData(); //不写 try-catch不行,不然代码走不下去!
setLoading(false);
};
==注意:我们在 axios 里面处理错误的时候必须要返回 Project.reject,否则默认会返回 resolve 的,这时候如果页面写了 try-catch 会不起作用,走不到 catch,不知道代码出了错误!会导致页面中代码逻辑意外向下执行!==
==而且页面不能不写 try-catch,必须写 try-catch,否则页面会直接产生意外报错,不太好!==
由于我们不能保证后端一定会处理每个错误(都写了 message)!有的还是我们自己处理会比较好!
所以下面是最好的写法
2.页面里面再次进行错误处理
这样 error可能是 js 代码错误,也可能是后端没有设置 message 时候的错误,我们在页面里面再进行弹窗!
try {
const res = await passwordLogin(
formData.email,
formData.password,
ticket,
randstr,
roleLogin
);
} catch (error) {
if (error) {
showSnackbar({
message: error.message || "Login failed. Please try again.",
severity: "error",
});
}
}
==注意:必须判断 error 是否存在,因为 showError 处理完的,还是会返回到页面中,会走到 catch 这里,走到了 catch 的 如果 error 存在,那么就是需要在页面里面处理 error!(可能是 js 代码错误,也可能是后端没有设置 message 时候的错误)==
总结最终的 axios 封装错误处理
封装:
service.interceptors.response.use(
(response) => {
const res = response?.data;
if (res?.code !== 200) {
console.log("response:", res, { response });
console.log("If code is not 200, an error is reported by default");
// 后端只要设置了code 就应该一定设置了 message
if (res?.message && res?.code) {
errorNotifier.showError(res.message);
return Promise.reject(); //必须返回Promise.reject
} else {
//如果没设置 message,那就让页面处理,但是这里 message 我们给空的
console.log("error:", res);
return Promise.reject(new Error(""));
}
} else {
return res;
}
},
(error) => {
// 这里只处理 axios 的 error
errorNotifier.showError(error?.message || "Error");
return Promise.reject(); //必须返回Promise.reject
}
);
页面:
重要的接口:需要处理后端没设置 message 的情况的(但是这个情况一般不存在)——> ==在公司写项目的时候最好这么写,后端没设置 message 的情况和 js 代码报错的情况都能弹窗出来==
try {
const res = await passwordLogin(
formData.email,
formData.password,
ticket,
randstr,
roleLogin
);
} catch (error) {
if (error) {
showSnackbar({
message: error.message || "Login failed. Please try again.",
severity: "error",
});
}
}
简单的接口:不需要弹窗的
try {
const res = await passwordLogin(
formData.email,
formData.password,
ticket,
randstr,
roleLogin
);
} catch (error) {
console.error(error?.message);
}
直接这样写,就算是后端没设置 message 我们也不管了(有报错可以在控制台看),只需要把 js 代码报错 console 出来就行!