前端架构实战:当服务器关闭时,如何优雅提示 502 错误?
在开发与运维过程中,后端服务重启或宕机是常有的事。此时,Nginx 会向浏览器返回 502 Bad Gateway 状态码。
默认情况下,Axios 会抛出 Request failed with status code 502 这样的原始错误信息。这对普通用户来说不仅晦涩难懂,还容易引起恐慌。作为前端架构师,我们需要在全局拦截器层面"消化"这些技术性错误,给用户展示友好的提示。
本文将带你优化 common.js 中的响应拦截器,实现对服务不可用状态的优雅处理。
一、 核心修复逻辑
我们需要在响应拦截器的异常处理函数中,对 HTTP 状态码进行分层处理。优先处理服务器故障类错误(502/503/504),其次是业务限流(429),最后才是常规的业务异常。
以下是优化后的 common.js 异常处理部分代码:
javascript
// ==================== B. 异常响应 (401, 429, 500, 502 等) ====================
async function (error) {
const originalRequest = error.config;
const response = error.response;
// -----------------------------------------------------
// 1. 服务器故障类拦截 (502, 503, 504)
// -----------------------------------------------------
if (response) {
const status = response.status;
// 502: 网关错误(后端服务未启动或Nginx连接不上后端)
// 503: 服务不可用(后端服务过载或正在维护)
if (status === 502 || status === 503) {
return Promise.reject("服务器正在维护或已关闭,请稍后再试");
}
// 504: 网关超时(后端处理时间过长)
if (status === 504) {
return Promise.reject("网络连接超时,请重试");
}
} else if (error.message.includes('Network Error')) {
// 服务器完全没响应(连 Nginx 都没开)或者本地断网
// 此时 response 对象不存在,需要通过 error.message 判断
return Promise.reject("无法连接到服务器,请检查网络");
}
// -----------------------------------------------------
// 2. 429 限流拦截
// -----------------------------------------------------
if (response && response.status === 429) {
const msg = (response.data && response.data.errorMsg)
? response.data.errorMsg
: "请求过于频繁,请稍后再试";
return Promise.reject(msg);
}
// -----------------------------------------------------
// 3. 401 主动刷新逻辑
// -----------------------------------------------------
if (
response &&
response.status === 401 &&
!originalRequest.url.includes("/user/refresh") &&
!originalRequest._retry
) {
// ... (此处保持你原有的 Token 刷新逻辑不变) ...
// 注意:如果服务端 502,上面已经拦截并 reject 了,不会走到这里。
// 只有状态码确实是 401 时才会进入此分支。
}
// -----------------------------------------------------
// 4. 最终兜底:状态码映射与默认提示
// -----------------------------------------------------
// 定义常见 HTTP 状态码的友好提示映射
const statusTextMap = {
400: "请求参数错误",
404: "请求资源不存在",
405: "请求方式不正确",
500: "服务器内部异常"
};
// 优先取后端返回的 errorMsg,其次取映射表文案,最后兜底
const finalMsg = (response && response.data && response.data.errorMsg)
? response.data.errorMsg
: (response && statusTextMap[response.status])
? statusTextMap[response.status]
: (error.message || "系统繁忙");
return Promise.reject(finalMsg);
}
二、 架构原理解析
为什么这样修改能解决问题?
1. 状态码优先级拦截
我们在代码最前端增加了对 502、503 的判断。一旦检测到这些状态码,直接返回固定的友好文案,阻断后续逻辑。这样避免了将这些错误传递到后续的业务逻辑中。
2. 区分"无响应"与"错误响应"
当服务器完全关闭(甚至连 Nginx 都没开)时,Axios 收不到 HTTP 响应,此时 error.response 为 undefined。
代码通过 error.message.includes('Network Error') 准确捕捉了这种底层网络中断的情况。
3. 状态映射表
通过 statusTextMap 对象,我们将常见的 HTTP 错误码(400, 404, 500)集中管理。如果后端没有返回详细的 errorMsg,前端会自动根据状态码显示"请求参数错误"或"服务器内部异常",而不是原生的英文堆栈信息。
4. 消除技术术语
原来的 error.message 可能包含 Request failed with status code... 这种技术术语。经过上述重构,用户看到的将只有你定义的中文提示语,体验更加统一。
三、 测试建议
修改完成后,建议进行以下测试以确保逻辑生效:
- 模拟 502:停掉 Spring Boot 后端服务,但保留 Nginx 运行。此时 Nginx 找不到后端会报 502。刷新前端点击按钮,应提示"服务器正在维护或已关闭,请稍后再试"。
- 模拟网络断开:直接把后端服务和 Nginx 全部关闭。点击按钮应提示"无法连接到服务器,请检查网络"。
- 清理缓存 :修改完 JS 文件后,记得在浏览器强制刷新(Ctrl + F5)清理缓存。
通过这种全局层面的容错处理,你的前端应用在面对后端服务波动时,将表现出更高的健壮性和专业度。