vue封装axios后,第一次初始化后就无法重新修改导致登陆后无法使用cookie保存的数据

本文介绍了在使用axios进行前后端通信时,登录后header值无法修改的问题,提出通过页面刷新和重新创建axios实例的方法来解决,并给出了具体实现步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

1.在工具文件中新建一个公共的request,定义了一请求头,但是在登录后,无法修改hearder的值。导致一些验证信息无法发送到后台完成验证。

    const request = axios.create({
      baseURL: host,
      headers: {
        role: 'phone',
        personId: personId,
      },
      withCredentials: true,
      crossDomain: true,
    })

解决办法

在登录默认跳转的界面先刷新一遍,重新 创建axios再请求

 const person = getPerson()
    if (person > 0) {
      try {
        this.getUsers()
      } catch (error) {
        this.$router.go(0)
        console.log('获取失败商品失败: ', error)
      }
    } else {
      UnLogin()
    }
<think>我们使用JavaScript(而不是TypeScript)来二次封装Axios,以便在不支持TypeScript的环境中使用封装目标:1.设置基础URL和超时时间2.请求拦截器:例如添加token等认证信息3.响应拦截器:统一处理响应数据和错误4.封装常用的HTTP方法(GET, POST, PUT, DELETE等)5.支持取消请求6.支持文件上传和下载我们将创建一个名为`http`的实例,并添加上述功能。代码结构:1.创建axios实例2.请求拦截器3.响应拦截器4.封装方法5.取消请求6.文件上传和下载注意:由于是JavaScript,我们不使用类型注解。</think>### 使用 JavaScript 二次封装 Axios 以下是完整的 Axios 二次封装方案,包含基础封装、拦截器管理、错误处理、请求取消等高级功能: ```javascript // api/http.js import axios from 'axios'; // 创建 Axios 实例 const createAxiosInstance = (baseURL) => { const instance = axios.create({ baseURL, timeout: 30000, // 30秒超时 headers: { 'Content-Type': 'application/json', }, withCredentials: true, // 携带凭证 }); // 请求拦截器 instance.interceptors.request.use( (config) => { // 添加认证令牌 const token = localStorage.getItem('access_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } // 显示加载提示 if (config.showLoading) { console.log('[API] 请求开始:', config.url); // 实际项目中可触发全局加载状态 } return config; }, (error) => { return Promise.reject(error); } ); // 响应拦截器 instance.interceptors.response.use( (response) => { // 隐藏加载提示 if (response.config.showLoading) { console.log('[API] 请求完成:', response.config.url); } // 处理标准响应格式 if (response.data && typeof response.data === 'object') { return response.data; } return response; }, (error) => { // 隐藏加载提示 if (error.config?.showLoading) { console.log('[API] 请求失败:', error.config.url); } // 自定义错误处理 if (typeof error.config?.errorHandler === 'function') { error.config.errorHandler(error); return Promise.reject(error); } // 默认错误处理 return handleError(error); } ); return instance; }; // 全局错误处理 const handleError = (error) => { if (error.response) { // 服务器返回错误 (4xx, 5xx) const { status, data } = error.response; const message = data?.message || `HTTP 错误 ${status}`; switch (status) { case 401: console.error('[API] 认证失败:', message); // 触发重新登录逻辑 break; case 403: console.error('[API] 权限不足:', message); break; case 404: console.error('[API] 资源不存在:', message); break; case 500: console.error('[API] 服务器错误:', message); break; default: console.error(`[API] 请求错误 (${status}):`, message); } } else if (error.request) { // 请求已发送但无响应 console.error('[API] 无服务器响应:', error.message); } else { // 请求配置错误 console.error('[API] 请求配置错误:', error.message); } return Promise.reject(error); }; // 创建 API 实例 const baseURL = process.env.VUE_APP_API_BASE_URL || '/api'; export const http = createAxiosInstance(baseURL); // 封装请求方法 export const get = (url, config = {}) => http.get(url, config); export const post = (url, data, config = {}) => http.post(url, data, config); export const put = (url, data, config = {}) => http.put(url, data, config); export const del = (url, config = {}) => http.delete(url, config); export const patch = (url, data, config = {}) => http.patch(url, data, config); // 文件上传 export const uploadFile = (url, file, fieldName = 'file', config = {}) => { const formData = new FormData(); formData.append(fieldName, file); return http.post(url, formData, { ...config, headers: { 'Content-Type': 'multipart/form-data', }, }); }; // 文件下载 export const downloadFile = (url, filename, config = {}) => { return http .get(url, { ...config, responseType: 'blob', }) .then((response) => { const blob = new Blob([response.data]); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; link.click(); URL.revokeObjectURL(link.href); }); }; // 请求取消功能 const cancelTokenMap = new Map(); export const createCancelToken = (key) => { // 如果已有相同key的请求,先取消 cancelRequest(key); const source = axios.CancelToken.source(); cancelTokenMap.set(key, source); return source.token; }; export const cancelRequest = (key, message = '请求被取消') => { const source = cancelTokenMap.get(key); if (source) { source.cancel(message); cancelTokenMap.delete(key); return true; } return false; }; export const cancelAllRequests = (message = '所有请求被取消') => { cancelTokenMap.forEach((source) => { source.cancel(message); }); cancelTokenMap.clear(); }; // 自动重试功能 export const setupRetryInterceptor = (instance, options = {}) => { const defaultOptions = { retries: 3, retryDelay: 1000, retryCondition: (error) => { // 仅对网络错误和超时重试 return !error.response || error.code === 'ECONNABORTED'; }, }; const mergedOptions = { ...defaultOptions, ...options }; instance.interceptors.response.use(null, async (error) => { const { config } = error; // 检查是否需要重试 if (!config || !mergedOptions.retryCondition(error)) { return Promise.reject(error); } // 初始化重试计数器 config.__retryCount = config.__retryCount || 0; // 检查是否达到最大重试次数 if (config.__retryCount >= mergedOptions.retries) { return Promise.reject(error); } // 增加重试计数器 config.__retryCount += 1; // 创建重试延迟 const delay = new Promise((resolve) => { setTimeout(() => resolve(true), mergedOptions.retryDelay); }); // 等待延迟后重试 return delay.then(() => instance(config)); }); }; // 初始化时添加重试拦截器 setupRetryInterceptor(http, { retries: 2 }); ``` ### 使用示例 #### 1. 基本请求 ```javascript import { get, post } from './api/http'; // GET 请求 get('/users') .then(response => console.log('用户列表:', response.data)) .catch(error => console.error('获取用户失败:', error)); // POST 请求 post('/login', { username: 'admin', password: 'password' }, { showLoading: true // 显示加载提示 }) .then(response => console.log('登录成功:', response.data)) .catch(error => console.error('登录失败:', error)); ``` #### 2. 文件上传 ```javascript import { uploadFile } from './api/http'; const fileInput = document.getElementById('file-input'); fileInput.addEventListener('change', (event) => { const file = event.target.files[0]; if (file) { uploadFile('/upload', file, 'document', { showLoading: true, onUploadProgress: (progressEvent) => { const percentCompleted = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); console.log(`上传进度: ${percentCompleted}%`); } }) .then(response => console.log('上传成功:', response.data)) .catch(error => console.error('上传失败:', error)); } }); ``` #### 3. 请求取消 ```javascript import { get, createCancelToken, cancelRequest } from './api/http'; // 发送可取消的请求 function searchProducts(query) { return get('/products/search', { params: { q: query }, cancelToken: createCancelToken('productSearch') }); } // 用户输入时触发搜索 const searchInput = document.getElementById('search-input'); let searchTimeout; searchInput.addEventListener('input', (event) => { const query = event.target.value.trim(); // 取消之前的请求 cancelRequest('productSearch'); if (query.length > 2) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { searchProducts(query) .then(response => displayResults(response.data)) .catch(error => { if (!axios.isCancel(error)) { console.error('搜索失败:', error); } }); }, 300); } }); ``` ### 高级功能:JWT 令牌自动刷新 ```javascript // 在 http.js 中添加 let isRefreshing = false; let refreshSubscribers = []; // 添加响应拦截器处理 401 错误 http.interceptors.response.use(null, (error) => { const { config, response } = error; if (response && response.status === 401 && !config._retry) { // 标记请求已重试 config._retry = true; if (!isRefreshing) { isRefreshing = true; // 刷新令牌 return http.post('/auth/refresh') .then((res) => { const newToken = res.data.token; localStorage.setItem('access_token', newToken); // 重试所有挂起的请求 refreshSubscribers.forEach((cb) => cb(newToken)); refreshSubscribers = []; // 重试原始请求 config.headers.Authorization = `Bearer ${newToken}`; return http(config); }) .catch((refreshError) => { // 刷新失败,跳转到登录页 console.error('令牌刷新失败:', refreshError); window.location.href = '/login'; return Promise.reject(refreshError); }) .finally(() => { isRefreshing = false; }); } else { // 将请求加入等待队列 return new Promise((resolve) => { refreshSubscribers.push((token) => { config.headers.Authorization = `Bearer ${token}`; resolve(http(config)); }); }); } } return Promise.reject(error); }); ``` ### 完整 API 服务层示例 ```javascript // api/services/authService.js import { post, get } from '../http'; export default { /** * 用户登录 * @param {Object} credentials - 登录凭证 * @param {string} credentials.username - 用户名 * @param {string} credentials.password - 密码 * @returns {Promise} 登录结果 */ login(credentials) { return post('/auth/login', credentials, { showLoading: true, skipErrorHandler: true // 跳过全局错误处理 }); }, /** * 获取当前用户信息 * @returns {Promise} 用户信息 */ getCurrentUser() { return get('/auth/me', { showLoading: true }); }, /** * 用户登出 * @returns {Promise} 登出结果 */ logout() { return post('/auth/logout'); }, /** * 刷新访问令牌 * @returns {Promise} 新的访问令牌 */ refreshToken() { return post('/auth/refresh', null, { skipErrorHandler: true // 跳过全局错误处理 }); } }; ``` ### 在 Vue/React 中使用 #### Vue 示例 ```javascript import { createApp } from 'vue'; import App from './App.vue'; import http from './api/http'; const app = createApp(App); app.config.globalProperties.$http = http; // 或者使用 provide/inject app.provide('http', http); app.mount('#app'); // 在组件中使用 export default { methods: { async fetchUsers() { try { const response = await this.$http.get('/users'); this.users = response.data; } catch (error) { console.error('获取用户失败:', error); } } } } ``` #### React 示例 ```javascript // 创建 Context import React from 'react'; import http from './api/http'; export const HttpContext = React.createContext(http); // 在 App 中提供 function App() { return ( <HttpContext.Provider value={http}> {/* 应用组件 */} </HttpContext.Provider> ); } // 在组件中使用 import { useContext } from 'react'; import { HttpContext } from './HttpContext'; function UserList() { const http = useContext(HttpContext); const [users, setUsers] = React.useState([]); React.useEffect(() => { const fetchUsers = async () => { try { const response = await http.get('/users'); setUsers(response.data); } catch (error) { console.error('获取用户失败:', error); } }; fetchUsers(); }, []); return ( <div> {/* 渲染用户列表 */} </div> ); } ``` ### 最佳实践建议 1. **环境配置**: ```javascript // .env.development VUE_APP_API_BASE_URL=http://localhost:3000/api // .env.production VUE_APP_API_BASE_URL=https://api.yourdomain.com/v1 ``` 2. **错误处理增强**: ```javascript // 全局捕获未处理的 Promise 错误 window.addEventListener('unhandledrejection', (event) => { const error = event.reason; if (error.isAxiosError) { // 显示全局错误通知 showErrorNotification(error.message); event.preventDefault(); } }); ``` 3. **性能监控**: ```javascript // 在请求拦截器中 http.interceptors.request.use((config) => { config.metadata = { startTime: Date.now() }; return config; }); // 在响应拦截器中 http.interceptors.response.use((response) => { const endTime = Date.now(); const duration = endTime - response.config.metadata.startTime; console.log(`请求 ${response.config.url} 耗时 ${duration}ms`); return response; }); ``` 4. **安全增强**: ```javascript // 防止 CSRF 攻击 http.interceptors.request.use((config) => { const csrfToken = getCSRFToken(); // 从 cookie 或 meta 标签获取 if (csrfToken) { config.headers['X-CSRF-Token'] = csrfToken; } return config; }); ``` ### 功能总结 1. **核心功能**: - 基础请求封装(GET/POST/PUT/DELETE) - 请求/响应拦截器 - 统一错误处理 - 文件上传/下载 - 请求取消 2. **高级功能**: - JWT 令牌自动刷新 - 请求重试机制 - 全局加载状态管理 - 性能监控 - 安全增强 3. **架构优势**: - 模块化设计 - 服务层分离 - 全局配置管理 - 跨框架兼容(Vue/React/纯JS
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值