React中请求拦截与响应拦截的统一处理方案

发布时间:2026/8/9 3:44:26
React中请求拦截与响应拦截的统一处理方案 1. 引言在现代前端开发中与后端API的交互是React应用的核心功能之一。随着应用复杂度提升对网络请求进行统一管理、添加全局逻辑如认证、错误处理、日志记录变得至关重要。请求拦截Request Interception和响应拦截Response Interception是实现这一目标的关键技术它们允许我们在请求发出前和响应返回后注入自定义逻辑从而实现代码复用、统一错误处理和提升开发效率。本文将深入探讨在React生态中实现请求与响应拦截的统一处理方案涵盖从基础概念到高级实践并提供完整的代码示例。2. 核心概念2.1 什么是请求拦截请求拦截是指在HTTP请求被发送到服务器之前对请求配置进行修改或添加额外逻辑的过程。常见的应用场景包括添加认证令牌自动在请求头中添加Authorization token统一请求头设置Content-Type、Accept等公共头部请求参数处理序列化数据、添加时间戳等请求日志记录请求信息用于调试和监控请求取消实现请求超时或取消逻辑2.2 什么是响应拦截响应拦截是指在接收到服务器响应后在数据传递给业务代码之前对响应进行处理的过程。常见的应用场景包括统一错误处理根据HTTP状态码或业务错误码进行全局错误处理数据格式化统一解析响应数据格式响应日志记录响应信息用于调试Token刷新在token过期时自动刷新并重试请求加载状态管理统一管理请求的加载状态3. 实现方案对比3.1 使用Axios拦截器Axios是目前最流行的HTTP客户端库之一内置了强大的拦截器机制。3.1.1 基础拦截器配置import axios from axios; // 创建axios实例 const apiClient axios.create({ baseURL: https://api.example.com, timeout: 10000, headers: { Content-Type: application/json, }, }); // 请求拦截器 apiClient.interceptors.request.use( (config) { // 在发送请求之前做些什么 const token localStorage.getItem(access_token); if (token) { config.headers.Authorization Bearer ${token}; } // 添加请求时间戳 config.headers[X-Request-Timestamp] Date.now(); // 记录请求日志开发环境 if (process.env.NODE_ENV development) { console.log([Request] ${config.method?.toUpperCase()} ${config.url}, config); } return config; }, (error) { // 对请求错误做些什么 console.error(Request interceptor error:, error); return Promise.reject(error); } ); // 响应拦截器 apiClient.interceptors.response.use( (response) { // 对响应数据做点什么 if (process.env.NODE_ENV development) { console.log([Response] ${response.status} ${response.config.url}, response.data); } // 统一处理响应格式 return { success: true, data: response.data, status: response.status, headers: response.headers, }; }, (error) { // 对响应错误做点什么 if (process.env.NODE_ENV development) { console.error([Response Error] ${error.response?.status || Network Error}, error); } // 统一错误处理 if (error.response) { // 服务器返回了错误状态码 switch (error.response.status) { case 401: // 未授权跳转到登录页 window.location.href /login; break; case 403: // 权限不足 console.error(权限不足请联系管理员); break; case 404: // 资源不存在 console.error(请求的资源不存在); break; case 500: // 服务器内部错误 console.error(服务器内部错误请稍后重试); break; default: console.error(请求失败: ${error.response.status}); } } else if (error.request) { // 请求已发出但没有收到响应 console.error(网络错误请检查网络连接); } else { // 请求配置出错 console.error(请求配置错误:, error.message); } return Promise.reject({ success: false, message: error.message, status: error.response?.status, data: error.response?.data, }); } ); export default apiClient;3.1.2 高级特性Token自动刷新let isRefreshing false; let failedQueue []; const processQueue (error, token null) { failedQueue.forEach(prom { if (error) { prom.reject(error); } else { prom.resolve(token); } }); failedQueue []; }; // 响应拦截器增强处理token过期 apiClient.interceptors.response.use( (response) response, async (error) { const originalRequest error.config; // 如果是401错误且不是刷新token的请求 if (error.response?.status 401 !originalRequest._retry) { if (isRefreshing) { // 如果正在刷新token将请求加入队列 return new Promise((resolve, reject) { failedQueue.push({ resolve, reject }); }).then(token { originalRequest.headers.Authorization Bearer ${token}; return apiClient(originalRequest); }).catch(err { return Promise.reject(err); }); } originalRequest._retry true; isRefreshing true; try { // 刷新token const refreshToken localStorage.getItem(refresh_token); const response await axios.post(/auth/refresh, { refreshToken }); const { access_token } response.data; // 保存新token localStorage.setItem(access_token, access_token); // 更新Authorization头 apiClient.defaults.headers.common[Authorization] Bearer ${access_token}; originalRequest.headers.Authorization Bearer ${access_token}; // 处理队列中的请求 processQueue(null, access_token); // 重试原始请求 return apiClient(originalRequest); } catch (refreshError) { // 刷新失败清空token并跳转到登录页 localStorage.removeItem(access_token); localStorage.removeItem(refresh_token); processQueue(refreshError, null); window.location.href /login; return Promise.reject(refreshError); } finally { isRefreshing false; } } return Promise.reject(error); } );3.2 使用Fetch API 自定义封装如果不希望引入第三方库可以使用原生Fetch API进行封装。class HttpClient { constructor(baseURL ) { this.baseURL baseURL; this.requestInterceptors []; this.responseInterceptors []; } // 添加请求拦截器 addRequestInterceptor(interceptor) { this.requestInterceptors.push(interceptor); return this; } // 添加响应拦截器 addResponseInterceptor(interceptor) { this.responseInterceptors.push(interceptor); return this; } // 执行请求拦截器链 async applyRequestInterceptors(config) { let result config; for (const interceptor of this.requestInterceptors) { result await interceptor(result); } return result; } // 执行响应拦截器链 async applyResponseInterceptors(response) { let result response; for (const interceptor of this.responseInterceptors) { result await interceptor(result); } return result; } // 核心请求方法 async request(url, options {}) { const config { url: this.baseURL url, ...options, headers: { Content-Type: application/json, ...options.headers, }, }; try { // 应用请求拦截器 const finalConfig await this.applyRequestInterceptors(config); // 发送请求 const response await fetch(finalConfig.url, finalConfig); // 应用响应拦截器 const finalResponse await this.applyResponseInterceptors(response); return finalResponse; } catch (error) { // 应用响应拦截器处理错误 for (const interceptor of this.responseInterceptors) { try { await interceptor(error, true); } catch (interceptorError) { console.error(Response interceptor error:, interceptorError); } } throw error; } } // 便捷方法 get(url, options {}) { return this.request(url, { ...options, method: GET }); } post(url, data, options {}) { return this.request(url, { ...options, method: POST, body: JSON.stringify(data), }); } put(url, data, options {}) { return this.request(url, { ...options, method: PUT, body: JSON.stringify(data), }); } delete(url, options {}) { return this.request(url, { ...options, method: DELETE }); } } // 使用示例 const httpClient new HttpClient(https://api.example.com); // 添加请求拦截器 httpClient.addRequestInterceptor(async (config) { const token localStorage.getItem(access_token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); // 添加响应拦截器 httpClient.addResponseInterceptor(async (response) { if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const data await response.json(); return { success: true, data, status: response.status, headers: response.headers, }; }); export default httpClient;3.3 使用React Query的全局配置React Query现为TanStack Query提供了强大的数据获取和缓存功能也支持全局配置拦截逻辑。import { QueryClient, QueryClientProvider } from tanstack/react-query; import axios from axios; // 配置axios实例 const apiClient axios.create({ baseURL: https://api.example.com, }); // 请求拦截器 apiClient.interceptors.request.use((config) { const token localStorage.getItem(access_token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); // 响应拦截器 apiClient.interceptors.response.use( (response) response, (error) { if (error.response?.status 401) { // 处理未授权 window.location.href /login; } return Promise.reject(error); } ); // 创建QueryClient并配置全局选项 const queryClient new QueryClient({ defaultOptions: { queries: { queryFn: async ({ queryKey }) { const [url, params] queryKey; const { data } await apiClient.get(url, { params }); return data; }, retry: (failureCount, error) { // 只在非401错误时重试 if (error.response?.status 401) { return false; } return failureCount 3; }, onError: (error) { // 全局查询错误处理 console.error(Query error:, error); }, }, mutations: { mutationFn: async (variables) { const { url, method POST, data } variables; const { data: responseData } await apiClient({ url, method, data, }); return responseData; }, onError: (error) { // 全局变更错误处理 console.error(Mutation error:, error); }, }, }, }); // 在React组件中使用 function App() { return ( QueryClientProvider client{queryClient} YourApp / /QueryClientProvider ); }4. 最佳实践与注意事项4.1 错误处理策略统一的错误处理是拦截器的核心价值之一// 错误类型定义 const ErrorType { NETWORK_ERROR: NETWORK_ERROR, TIMEOUT_ERROR: TIMEOUT_ERROR, SERVER_ERROR: SERVER_ERROR, CLIENT_ERROR: CLIENT_ERROR, VALIDATION_ERROR: VALIDATION_ERROR, AUTH_ERROR: AUTH_ERROR, }; // 统一错误处理拦截器 const errorHandlerInterceptor (error) { let errorType ErrorType.NETWORK_ERROR; let userMessage 网络错误请检查网络连接; if (error.response) { // 服务器响应了错误状态码 const status error.response.status; if (status 500) { errorType ErrorType.SERVER_ERROR; userMessage 服务器内部错误请稍后重试; } else if (status 400) { errorType ErrorType.CLIENT_ERROR; switch (status) { case 400: userMessage 请求参数错误; break; case 401: errorType ErrorType.AUTH_ERROR; userMessage 登录已过期请重新登录; break; case 403: userMessage 权限不足无法访问该资源; break; case 404: userMessage 请求的资源不存在; break; case 422: errorType ErrorType.VALIDATION_ERROR; userMessage 数据验证失败; break; default: userMessage 请求失败: ${status}; } } } else if (error.code ECONNABORTED) { errorType ErrorType.TIMEOUT_ERROR; userMessage 请求超时请稍后重试; } // 统一错误格式 const formattedError { type: errorType, message: userMessage, originalError: error, timestamp: new Date().toISOString(), }; // 根据环境处理错误 if (process.env.NODE_ENV development) { console.error(Request Error:, formattedError); } // 可以在这里集成错误上报服务 // reportErrorToService(formattedError); // 显示用户友好的错误提示 showNotification(userMessage, error); return Promise.reject(formattedError); }; // 在响应拦截器中注册 apiClient.interceptors.response.use( (response) response, errorHandlerInterceptor );4.2 性能监控与日志// 性能监控拦截器 const performanceInterceptor { request: (config) { config.metadata { startTime: Date.now() }; return config; }, response: (response) { const endTime Date.now(); const startTime response.config.metadata?.startTime; if (startTime) { const duration endTime - startTime; console.log(请求 ${response.config.url} 耗时: ${duration}ms); // 慢请求警告 if (duration 5000) { console.warn(慢请求警告: ${response.config.url} 耗时 ${duration}ms); } // 可以上报到监控系统 // reportPerformanceMetrics({ // url: response.config.url, // method: response.config.method, // duration, // status: response.status, // }); } return response; }, }; // 请求日志拦截器 const loggingInterceptor { request: (config) { console.group( 请求: ${config.method?.toUpperCase()} ${config.url}); console.log(请求配置:, config); console.groupEnd(); return config; }, response: (response) { console.group( 响应: ${response.status} ${response.config.url}); console.log(响应数据:, response.data); console.log(响应头:, response.headers); console.groupEnd(); return response; }, error: (error) { console.group(❌ 错误: ${error.config?.url}); console.error(错误详情:, error); console.groupEnd(); return Promise.reject(error); }, }; // 注册拦截器 apiClient.interceptors.request.use( performanceInterceptor.request, loggingInterceptor.error ); apiClient.interceptors.request.use(loggingInterceptor.request); apiClient.interceptors.response.use( performanceInterceptor.response, loggingInterceptor.error ); apiClient.interceptors.response.use(loggingInterceptor.response);4.3 请求取消与防抖// 请求取消管理器 class RequestCanceler { const