JavaScript/React生态调用后端API时的统一错误处理方案咨询
React Native 网络请求统一错误处理方案
你当前写法的核心冗余点在于:client层、接口定义层的try/catch没有实现任何实际处理逻辑,仅做了二次错误抛出,属于完全可以删除的无效代码。
你需要的类express中间件的能力,Axios本身已经通过拦截器提供,可以基于拦截器实现全链路通用错误统一处理,再配合可选的响应包装消除业务层的try/catch嵌套。
第一步:重构client.js,基于Axios拦截器实现统一错误处理
// client.js import axios from 'axios'; // 1. 创建自定义Axios实例,统一配置基础参数 const apiClient = axios.create({ baseURL: domain, // 直接配置基础域名,无需每次拼接 timeout: 15000, // 统一配置超时时间 // 其他通用配置如全局请求头都可以在此处定义 }); // 2. 请求拦截器:统一处理请求前逻辑,如加认证token、签名等 apiClient.interceptors.request.use( (config) => { // 示例:统一注入登录token const userToken = getTokenFromLocalStorage(); if (userToken) config.headers.Authorization = `Bearer ${userToken}`; return config; }, (error) => Promise.reject(error) ); // 3. 响应拦截器:核心统一错误处理逻辑,对应你需要的express中间件能力 apiClient.interceptors.response.use( // 请求成功直接返回响应数据,后续层级无需重复取`.data` (response) => response.data, (error) => { // 此处统一处理所有通用错误,无需业务层重复实现 if (error.response) { // 服务端返回错误状态码 const { status, data } = error.response; switch (status) { case 401: // 统一处理未授权:清空用户信息、跳登录页、全局提示 clearUserStorage(); navigation.navigate('Login'); showToast('登录状态已过期,请重新登录'); break; case 403: showToast('你没有权限执行该操作'); break; case 500: showToast('服务器异常,请稍后重试'); break; default: // 优先使用服务端返回的错误提示 showToast(data?.message || '请求失败,请稍后重试'); } } else if (error.request) { // 请求发出未收到响应:网络异常、服务端不可用等 showToast('网络异常,请检查网络连接后重试'); } else { // 请求配置错误 showToast('请求参数异常,请重试'); } // 若有需要业务层特殊处理的错误,此处继续抛出即可,无需特殊处理可直接return undefined return Promise.reject(error); } ); // 封装请求方法,无需多余try/catch export const get = (endpoint, config = {}) => apiClient.get(endpoint, config); export const post = (endpoint, data = {}, config = {}) => apiClient.post(endpoint, data, config); // 同理可封装put、delete等方法
第二步:简化接口定义层代码,删除冗余try/catch
// posts.js import { get } from './client'; const getPosts = async (userID, page, pageSize) => { // 使用axios自带的params配置,无需手动拼接query字符串,避免编码问题 return get(endpoint, { params: { userID, page, pageSize } }); };
第三步:业务层调用的两种可选写法
写法1:仅需特殊处理错误时保留try/catch
普通场景下通用错误已经在拦截器处理完成,不需要额外加try/catch,只有该接口需要做特殊错误逻辑时才添加:
const loadPosts = async () => { try { const data = await getPosts(1, 1, 10); // 直接拿到服务端返回的业务数据,无需再取`.data` setPosts(data); } catch (error) { // 仅写该接口专属的错误处理逻辑,比如自定义错误提示展示位置 setListError('帖子加载失败,请下拉刷新重试'); } };
写法2:用元组包装完全消除try/catch
如果不想写try/catch,可以封装一个通用请求包装函数,返回类似Go语言的[数据, 错误]元组:
// 在client.js中添加通用包装方法 export const withError = async (requestPromise) => { try { const data = await requestPromise; return [data, null]; } catch (error) { return [null, error]; } };
业务层调用:
const loadPosts = async () => { const [data, error] = await withError(getPosts(1, 1, 10)); if (error) { setListError('帖子加载失败,请下拉刷新重试'); return; } setPosts(data); };
额外优化建议
如果需要进一步简化加载状态、缓存、重试等逻辑,可以引入React Query/SWR这类数据请求库,全局错误处理也可以通过库的配置统一实现,无需自己维护loading、error等组件状态。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

