如何實(shí)現(xiàn)axios的自定義適配器adapter
1. 適配器要實(shí)現(xiàn)的功能
我們?cè)诨?axios 實(shí)現(xiàn)額外的數(shù)據(jù)模塊時(shí),應(yīng)當(dāng)與 axios 的模式進(jìn)行對(duì)齊。因此在返回的數(shù)據(jù)格式上,實(shí)現(xiàn)的功能上盡量保持一致。
1.1 promise 和工具
所有的適配均應(yīng)當(dāng)實(shí)現(xiàn)為 Promise 方式。
而且,有些功能的實(shí)現(xiàn),axios 將其下放到了適配器中自己進(jìn)行實(shí)現(xiàn),例如
- url 的拼接:即 baseURL 和 url 的拼接,若存在 baseURL 且 url 為相對(duì)路徑,則進(jìn)行拼接,否則直接使用 url;
- 參數(shù)的拼接:若是 get 請(qǐng)求,需要自行將 object 類型拼接為 url 參數(shù)的格式并與 url 拼接完成;
這是自己需要實(shí)現(xiàn)的兩個(gè)基本的工具方法。
1.2 響應(yīng)的格式
這里我們要注意到請(qǐng)求接口正常和異常的格式。
接口正常時(shí):
const result = { status: 200, // 接口的http 狀態(tài) statusText: 'ok', config: 'config', // 傳入的config配置,原樣返回即可,方便在響應(yīng)攔截器和響應(yīng)結(jié)果中使用 data: {}, // 真實(shí)的接口返回結(jié)果 };
接口異常時(shí),我們可以看下 axios 源碼中對(duì)錯(cuò)誤信息的處理createError,enhanceError(createError 中調(diào)用了 enhanceError),首先會(huì)創(chuàng)建一個(gè) error 實(shí)例,然后給這個(gè) error 實(shí)例添加一個(gè)屬性:
module.exports = function enhanceError(error, config, code, request, response) { error.config = config; if (code) { error.code = code; } error.request = request; error.response = response; error.isAxiosError = true; error.toJSON = function toJSON() { return { // Standard message: this.message, name: this.name, // Microsoft description: this.description, number: this.number, // Mozilla fileName: this.fileName, lineNumber: this.lineNumber, columnNumber: this.columnNumber, stack: this.stack, // Axios config: this.config, code: this.code, }; }; return error; };
可以看到,除了正常的錯(cuò)誤信息外,還加入了很多別的屬性,例如 request, response, config 等。這里我們?cè)谧约簩?shí)現(xiàn)適配器時(shí),最好也要這樣統(tǒng)一編寫(xiě),方便更上層的業(yè)務(wù)層統(tǒng)一處理,避免為單獨(dú)的適配器進(jìn)行特殊處理。
關(guān)于 1.1 和 1.2 中的內(nèi)容,若不進(jìn)行打包編譯,則需要自己實(shí)現(xiàn)。若還要通過(guò) webpack 等打包工具編譯一下的,可以直接引用 axios 中的方法,不用自己實(shí)現(xiàn)了,參考官方基于 axios 實(shí)現(xiàn)的mock-axios。例如:
import axios from 'axios'; import buildURL from 'axios/lib/helpers/buildURL'; import isURLSameOrigin from 'axios/lib/helpers/isURLSameOrigin'; import btoa from 'axios/lib/helpers/btoa'; import cookies from 'axios/lib/helpers/cookies'; import settle from 'axios/lib/core/settle'; import createError from 'axios/lib/core/createError';
然后直接使用就行了,不用再進(jìn)行二次開(kāi)發(fā)。
1.3 超時(shí)設(shè)置
我們不能無(wú)限地等待第三方服務(wù)的響應(yīng),如果第三方服務(wù)無(wú)響應(yīng)或者響應(yīng)時(shí)間過(guò)長(zhǎng),應(yīng)當(dāng)適時(shí)的終止掉。在 axios 中,前端使用了XMLHttpRequest,在 node 端使用了http,來(lái)實(shí)現(xiàn)接口的請(qǐng)求,兩者都有超時(shí)的設(shè)定,可以設(shè)置 timeout 字段來(lái)設(shè)置超時(shí)的時(shí)間,自動(dòng)取消當(dāng)前的請(qǐng)求。
像有的發(fā)起的請(qǐng)求,自己并沒(méi)有超時(shí)的設(shè)定,例如 jsonp,是用創(chuàng)建一個(gè) script 標(biāo)簽來(lái)發(fā)起的請(qǐng)求,這個(gè)請(qǐng)求必須等到服務(wù)器有響應(yīng)才會(huì)終止(成功或者失?。_@時(shí),就需要我們自己用一個(gè)setTimeout來(lái)模擬了,但這樣,即使返回給業(yè)務(wù)層說(shuō)“超時(shí)了,已取消當(dāng)前請(qǐng)求”,但實(shí)際上請(qǐng)求還在,只不過(guò)若超過(guò)規(guī)定時(shí)間,只是不再執(zhí)行對(duì)應(yīng)的成功操作而已。
1.4 主動(dòng)取消請(qǐng)求
我們也會(huì)有很多并沒(méi)有到超時(shí)時(shí)間,就需要主動(dòng)取消當(dāng)前請(qǐng)求的場(chǎng)景,例如在請(qǐng)求返回之前就切換了路由;上次請(qǐng)求還沒(méi)響應(yīng)前,又需要發(fā)出新的請(qǐng)求等。都需要主動(dòng)地取消當(dāng)前請(qǐng)求。
axios 中已經(jīng)提供了取消請(qǐng)求的功能,我們只需要按照規(guī)則接入即可。我們來(lái)看下 XMLHttpRequest 請(qǐng)求器中是怎么取消請(qǐng)求的,在寫(xiě)自定義請(qǐng)求器時(shí)也可以照理使用。
在lib/adapters/xhr.js#L158中:
// 若config中已經(jīng)配置了cancelToken if (config.cancelToken) { // Handle cancellation // 若在外城執(zhí)行了取消請(qǐng)求的方法,則這里將當(dāng)前的請(qǐng)求取消掉 config.cancelToken.promise.then(function onCanceled(cancel) { if (!request) { return; } // xhr中使用abort方法取消當(dāng)前請(qǐng)求 request.abort(); reject(cancel); // Clean up request request = null; }); }
我們?cè)趯?xiě)自己的適配器時(shí),也可以將這段拷貝過(guò)去,將內(nèi)部取消的操作更換為自己的即可。
到這里,若把上面的功能都實(shí)現(xiàn)了,就已經(jīng)完成了一個(gè)標(biāo)準(zhǔn)的適配器了。
2. 編寫(xiě)自定義適配器
每個(gè)人需要的適配器肯定也不一樣,復(fù)雜度也不一樣,例如有的想接入小程序的請(qǐng)求,我自己想接入客戶端里提供的數(shù)據(jù)請(qǐng)求方式等。我們這里只是通過(guò)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的jsonp適配器來(lái)講解下實(shí)現(xiàn)方式。
我們以 es6 的模塊方式來(lái)進(jìn)行開(kāi)發(fā)。所有的實(shí)現(xiàn)均在代碼中進(jìn)行了講解。
// 這里的config是axios里所有的配置 const jsonpAdapter = (config) => { return new Promise((resolve, reject) => { // 是否已取消當(dāng)前操作 // 因jsonp沒(méi)有主動(dòng)取消請(qǐng)求的方式 // 這里使用 isAbort 來(lái)標(biāo)識(shí) let isAbort = false; // 定時(shí)器標(biāo)識(shí)符 let timer = null; // 執(zhí)行方法的名字, const callbackName = `jsonp${Date.now()}_${Math.random() .toString() .slice(2)}`; // 這里假設(shè)已經(jīng)實(shí)現(xiàn)了baseURL和url的拼接方法 const fullPath = buildFullPath(config.baseURL, config.url); // 這里假設(shè)已經(jīng)實(shí)現(xiàn)了url和參數(shù)的拼接方法 // 不太一樣的地方在于,jsonp需要額外插入一個(gè)自己的回調(diào)方法 const url = buildURL( fullPath, { ...config.params, ...{ [config.jsonpCallback || 'callback']: callbackName }, }, config.paramsSerializer ); // 創(chuàng)建一個(gè)script標(biāo)簽 let script = document.createElement('script'); // 成功執(zhí)行操作后 function remove() { if (script) { script.onload = script.onerror = null; // 移除script標(biāo)簽 if (script.parentNode) { script.parentNode.removeChild(script); } // 取消定時(shí)器 if (timer) { clearTimeout(timer); } script = null; } } // 成功請(qǐng)求后 window[callbackName] = (data) => { // 若已需要請(qǐng)求,則不再執(zhí)行 if (isAbort) { return; } // 返回的格式 const response = { status: 200, statusText: 'ok', config, request: script, data: data, }; remove(); // 實(shí)際上這里上一個(gè)settle操作,會(huì)額外判斷是否是合理的status狀態(tài) // 若我們?cè)赾onfig.validateStatus中設(shè)置404是合理的,也會(huì)進(jìn)入到resolve狀態(tài) // 但我們這里就不實(shí)現(xiàn)這個(gè)了 // settle(resolve, reject, response); resolve(response); }; // 請(qǐng)求失敗 script.onerror = function (error) { remove(); reject(createError('Network Error', config, 404)); }; // 若設(shè)置了超時(shí)時(shí)間 if (config.timeout) { timer = setTimeout(function () { remove(); // 取消當(dāng)前操作 isAbort = true; reject( createError( 'timeout of ' + config.timeout + 'ms exceeded', config, 405 ) ); }, config.timeout); } // 若定義了取消操作 if (config.cancelToken) { config.cancelToken.promise.then(function () { if (!script) { return; } remove(); isAbort = true; reject(createError('Cancel Error', config, 404)); }); } script.src = url; const target = document.getElementsByTagName('script')[0] || document.head; target.parentNode && target.parentNode.insertBefore(script, target); }); }; export default jsonpAdapter;
3. 將適配器添加到 axios 中
axios 的 config 提供了 adapter 字段讓我們插入自己的適配器。使用自定義適配器又有兩種情況:
1.完全只使用自定義的適配器;
2.在某種情況下使用自定義適配器,其他情況時(shí)還是使用 axios 自己的適配器。
第 1 種情況還好,只需要 return 自己適配器返回的結(jié)果結(jié)果即可;而第 2 種情況中,則有個(gè)小坑需要踩一下,我們這里也只講解下第 2 種情況。我要把剛才實(shí)現(xiàn)的 jsonp 適配器添加到 axios 中,并且只在參數(shù)有format=jsonp時(shí)才調(diào)用該適配器,其他還是用的 axios 提供的適配器。
import Axios from 'axios'; import jsonpAdapter from './jsonpAdater'; const request = Axios.create({ adapter: (config) => { if (config?.params?.format === 'jsonp') { return jsonpAdapter(config); } // 這里需要將config.adapter設(shè)置為空 // 否則會(huì)造成無(wú)限循環(huán) return defaultAxios({ ...config, ...{ adapter: undefined } }); }, });
使用自定義的適配器 jsonp 發(fā)起請(qǐng)求。
// 使用自定義的適配器jsonp發(fā)起請(qǐng)求 var options = { params: { format: 'jsonp', }, }; request( 'https://api.prize.qq.com/v1/newsapp/answer/share/oneQ?qID=506336', options ) .then(function (response) { console.log('jsonp response', response); }) .catch(function (error) { console.error('jsonp error', error); });
使用 axios 默認(rèn)的適配器發(fā)起請(qǐng)求。
// 使用axios默認(rèn)的適配器發(fā)起請(qǐng)求 request('https://api.prize.qq.com/v1/newsapp/answer/share/oneQ?qID=506336') .then(function (response) { console.log('axios response', response); }) .catch(function (error) { console.error('axios error', error); });
4. 總結(jié)
這里,我們就已經(jīng)實(shí)現(xiàn)了一個(gè)自定義適配器了,在滿足一定條件時(shí)可以觸發(fā)這個(gè)適配器。通過(guò)這個(gè)思路,我們也可以實(shí)現(xiàn)一個(gè)自定義的 mock 方法,例如當(dāng)參數(shù)中包含format=mock時(shí)則調(diào)用 mock 接口,否則就正常請(qǐng)求。
以上就是如何實(shí)現(xiàn)axios的自定義適配器adapter的詳細(xì)內(nèi)容,更多關(guān)于axios自定義適配器adapter的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
DD_belatedPNG,IE6下PNG透明解決方案(國(guó)外)
今天介紹DD_belatedPNG,只需要一個(gè)理由,就是它支持backgrond-position與background-repeat.這是其他js插件不具備的.2010-12-12layui使用button按鈕 點(diǎn)擊出現(xiàn)彈層 彈層中加載表單的實(shí)例
今天小編就為大家分享一篇layui使用button按鈕 點(diǎn)擊出現(xiàn)彈層 彈層中加載表單的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09uniapp實(shí)現(xiàn)全局變量的幾種方式總結(jié)
這里說(shuō)全局變量,著重指的是能夠全局動(dòng)態(tài)響應(yīng)的情況,下面這篇文章主要給大家介紹了關(guān)于uniapp實(shí)現(xiàn)全局變量的幾種方式,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10利用JQuery和JS實(shí)現(xiàn)奇偶行背景顏色自定義效果
本文將詳細(xì)介紹利用JQuery和JS實(shí)現(xiàn)奇偶行背景顏色自定義效果,需要的朋友可以參考下2012-11-11JS實(shí)現(xiàn)的找零張數(shù)最小問(wèn)題示例
這篇文章主要介紹了JS實(shí)現(xiàn)的找零張數(shù)最小問(wèn)題,涉及javascript數(shù)學(xué)運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2017-11-11js+html5實(shí)現(xiàn)可在手機(jī)上玩的拼圖游戲
這篇文章主要介紹了js+html5實(shí)現(xiàn)可在手機(jī)上玩的拼圖游戲,涉及javascript結(jié)合html5進(jìn)行圖形操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07