axios的interceptors多次執行問題解決
問題
在進行 axios
封裝的時候,遇到個問題,就是每次發起請求時axios 都會執行兩次響應攔截,甚是納悶,一時理不出思路來。
代碼如下:
class Http { constructor(config) { this.axios = axios; this.axiosInterceptor = undefined; // 公共的 header let defaultHeaders = { 'Content-Type': 'application/json;charset=UTF-8', 'Accept': 'application/json', // 通過頭指定,獲取的數據類型是JSON 'application/json, text/plain, */*', 'x-end-point': '.10.' } if(config?.headers){ for (let i in config.headers) { defaultHeaders[i] = config.headers[i]; } } axios({ // `baseURL` 將自動加在 `url` 前面,除非 `url` 是一個絕對 URL。 // 它可以通過設置一個 `baseURL` 便於為 axios 實例的方法傳遞相對 URL baseURL: config?.baseURL, // `url` 是用於請求的服務器 URL url: config?.url, // `method` 是創建請求時使用的方法 method: config?.method || 'get', // `headers` 是即將被發送的自定義請求頭 headers: {...defaultHeaders}, // `params` 是即將與請求一起發送的 URL 參數 // 必須是一個無格式對象(plain object)或 URLSearchParams 對象 params: config?.method === 'get' ? config?.params || {} : {}, // `paramsSerializer` 是一個負責 `params` 序列化的函數 // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/) paramsSerializer: function(params) { return Qs.stringify(params, {arrayFormat: 'brackets'}) }, // `data` 是作為請求主體被發送的數據 // 隻適用於這些請求方法 'PUT', 'POST', 和 'PATCH' // 在沒有設置 `transformRequest` 時,必須是以下類型之一: // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams // - 瀏覽器專屬:FormData, File, Blob // - Node 專屬: Stream data: config?.method === 'post' ? config?.params || {} : {}, // `timeout` 指定請求超時的毫秒數(0 表示無超時時間) // 如果請求話費瞭超過 `timeout` 的時間,請求將被中斷 timeout: 0, // `withCredentials` 表示跨域請求時是否需要使用憑證 withCredentials: false, // default 為true則產生跨域,跨域攜帶cookie // `responseType` 表示服務器響應的數據類型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' responseType: 'json', // default }); // 添加請求攔截器 axios.interceptors.request.use( (config) => { // 在發送請求之前做些什麼 return config; }, function (error) { // 對請求錯誤做些什麼 return Promise.reject(error); }); // 添加響應攔截器 axios.interceptors.response.use((res) => { const { status, data } = res; // 對錯誤狀態提示進行處理 let message = '' if (status < 200 || status >= 300) { // 處理http錯誤,拋到業務代碼 message = this.showResState(status) if (typeof res.data === 'string') { res.data = {code:status, message } } else { res.data.code = status res.data.message = message } } return res.data; }, function (error) { // 對響應錯誤做點什麼 return Promise.reject(error); }); } get(url,params={}){ // 為給定 ID 的 user 創建請求 return new Promise((resolve, reject) => { this.axios.get(url,{ params }).then(response => { // 2. 如果成功瞭, 調用resolve(value) resolve(response); }) .catch(error => { // 3. 如果失敗瞭, 不調用reject(reason), 而是提示異常信息 reject(error) // message.error('請求出錯瞭: ' + error.message).then(r => {}); }).finally(() => { }) }); } post(url,params={}){ return new Promise((resolve, reject) => { this.axios.post(url, params).then(response => { // 2. 如果成功瞭, 調用resolve(value) resolve(response); }) .catch(error => { // 3. 如果失敗瞭, 不調用reject(reason), 而是提示異常信息 reject(error) // message.error('請求出錯瞭: ' + error.message).then(r => {}); }).finally(() => { }) }); } showResState (state) { let message = ''; // 這裡隻做部分常見的示例,具體根據需要進行配置 switch (state) { case 400: message = '請求錯誤(400)' break case 401: message = '未授權,請重新登錄(401)' break case 403: message = '拒絕訪問(403)' break case 404: message = '請求出錯(404)' break case 500: message = '服務器錯誤(500)' break case 501: message = '服務未實現(501)' break case 502: message = '網絡錯誤(502)' break case 503: message = '服務不可用(503)' break default: message = `連接出錯(${state})!` } return `${message},請檢查網絡或聯系網站管理員!` } // 插件初始化時會傳入所需的配置項 autoAddToken (config) { // 在請求階段時修改 config 配置項為其添加 token 具體屬性名稱可自定義 config.headers ??= {} config.headers.Authorization = localStorage.token || null return config } } export default Http;
可能遇到過該類問題的小夥伴們一眼就看出問題所在,對於未有碰到過這個問題的小夥伴,可能就又點煎熬瞭。
原因
若你使用use,就像Node.js裡的use那樣,會不斷地往axios對象添加interceptors,由於我將該攔截器放在函數內,隻要函數被執行,則會再次將攔截器函數增添到axios對象上。
所以,推薦的辦法是,將攔截器放在函數外,可我的需求決定瞭,我必須將它放在函數內,那麼該如何解決呢?
解決
添加該文件內的唯一變量標識符let interceptor = null,進行判斷,隻要攔截器存在,則不會繼續添加,部分代碼如下所示:
if (!this.interceptor) { // 添加響應攔截器 this.interceptor = axios.interceptors.response.use((res) => { const { status, data } = res; // 對錯誤狀態提示進行處理 let message = '' if (status < 200 || status >= 300) { // 處理http錯誤,拋到業務代碼 message = this.showResState(status) if (typeof res.data === 'string') { res.data = {code:status, message } } else { res.data.code = status res.data.message = message } } return res.data; }, function (error) { // 對響應錯誤做點什麼 return Promise.reject(error); }); }
奈何不好使。不得一不得不把攔截器提取到類外部,問題解決。
這裡隻貼部分主要代碼:
import axios from "axios"; /* 將攔截器 置於封裝類之外 */ // 添加請求攔截器 axios.interceptors.request.use( (config) => { // 在發送請求之前做些什麼 添加 token 等鑒權功能 //... return config; }, function (error) { // 對請求錯誤做些什麼 return Promise.reject(error); }); // 添加響應攔截器 axios.interceptors.response.use((res) => { const { status } = res; // 在發送結果之前做些什麼 對錯誤狀態提示進行處理 //... return res.data; }, function (error) { // 對響應錯誤做點什麼 return Promise.reject(error); }); class Http { constructor(config) { this.axios = axios; // 這裡仍需對配置進行處理,代碼省略瞭 this.config = config; } // Get 請求 get(url,params={},headers={}){ // ... } // POST 請求 post(url,params={},headers={}){ // ... } } export default Http; // 無特殊需求的隻需使用這個一個對象即可 公共 header 可在此配置, 如需多個實例 可按照此方式創建多個進行導出 export const Axios = new Http({ headers: { 'x-http-token': 'xxx' } });
這裡不對具體的方法進行描述,隻做一個解決問題的說明,後續會針對 axios 類的封裝,單獨寫篇文章再詳細說明下的
以上就是axios的interceptors多次執行問題解決的詳細內容,更多關於axios interceptors多次執行的資料請關註WalkonNet其它相關文章!