如何實現(xiàn)axios的自定義適配器adapter
1. 適配器要實現(xiàn)的功能
我們在基于 axios 實現(xiàn)額外的數(shù)據(jù)模塊時,應(yīng)當(dāng)與 axios 的模式進行對齊。因此在返回的數(shù)據(jù)格式上,實現(xiàn)的功能上盡量保持一致。
1.1 promise 和工具
所有的適配均應(yīng)當(dāng)實現(xiàn)為 Promise 方式。
而且,有些功能的實現(xiàn),axios 將其下放到了適配器中自己進行實現(xiàn),例如
- url 的拼接:即 baseURL 和 url 的拼接,若存在 baseURL 且 url 為相對路徑,則進行拼接,否則直接使用 url;
- 參數(shù)的拼接:若是 get 請求,需要自行將 object 類型拼接為 url 參數(shù)的格式并與 url 拼接完成;
這是自己需要實現(xiàn)的兩個基本的工具方法。
1.2 響應(yīng)的格式
這里我們要注意到請求接口正常和異常的格式。
接口正常時:
const result = {
status: 200, // 接口的http 狀態(tài)
statusText: 'ok',
config: 'config', // 傳入的config配置,原樣返回即可,方便在響應(yīng)攔截器和響應(yīng)結(jié)果中使用
data: {}, // 真實的接口返回結(jié)果
};
接口異常時,我們可以看下 axios 源碼中對錯誤信息的處理createError,enhanceError(createError 中調(diào)用了 enhanceError),首先會創(chuàng)建一個 error 實例,然后給這個 error 實例添加一個屬性:
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;
};
可以看到,除了正常的錯誤信息外,還加入了很多別的屬性,例如 request, response, config 等。這里我們在自己實現(xiàn)適配器時,最好也要這樣統(tǒng)一編寫,方便更上層的業(yè)務(wù)層統(tǒng)一處理,避免為單獨的適配器進行特殊處理。
關(guān)于 1.1 和 1.2 中的內(nèi)容,若不進行打包編譯,則需要自己實現(xiàn)。若還要通過 webpack 等打包工具編譯一下的,可以直接引用 axios 中的方法,不用自己實現(xiàn)了,參考官方基于 axios 實現(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';
然后直接使用就行了,不用再進行二次開發(fā)。
1.3 超時設(shè)置
我們不能無限地等待第三方服務(wù)的響應(yīng),如果第三方服務(wù)無響應(yīng)或者響應(yīng)時間過長,應(yīng)當(dāng)適時的終止掉。在 axios 中,前端使用了XMLHttpRequest,在 node 端使用了http,來實現(xiàn)接口的請求,兩者都有超時的設(shè)定,可以設(shè)置 timeout 字段來設(shè)置超時的時間,自動取消當(dāng)前的請求。
像有的發(fā)起的請求,自己并沒有超時的設(shè)定,例如 jsonp,是用創(chuàng)建一個 script 標簽來發(fā)起的請求,這個請求必須等到服務(wù)器有響應(yīng)才會終止(成功或者失敗)。這時,就需要我們自己用一個setTimeout來模擬了,但這樣,即使返回給業(yè)務(wù)層說“超時了,已取消當(dāng)前請求”,但實際上請求還在,只不過若超過規(guī)定時間,只是不再執(zhí)行對應(yīng)的成功操作而已。
1.4 主動取消請求
我們也會有很多并沒有到超時時間,就需要主動取消當(dāng)前請求的場景,例如在請求返回之前就切換了路由;上次請求還沒響應(yīng)前,又需要發(fā)出新的請求等。都需要主動地取消當(dāng)前請求。
axios 中已經(jīng)提供了取消請求的功能,我們只需要按照規(guī)則接入即可。我們來看下 XMLHttpRequest 請求器中是怎么取消請求的,在寫自定義請求器時也可以照理使用。
在lib/adapters/xhr.js#L158中:
// 若config中已經(jīng)配置了cancelToken
if (config.cancelToken) {
// Handle cancellation
// 若在外城執(zhí)行了取消請求的方法,則這里將當(dāng)前的請求取消掉
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
// xhr中使用abort方法取消當(dāng)前請求
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
我們在寫自己的適配器時,也可以將這段拷貝過去,將內(nèi)部取消的操作更換為自己的即可。
到這里,若把上面的功能都實現(xiàn)了,就已經(jīng)完成了一個標準的適配器了。
2. 編寫自定義適配器
每個人需要的適配器肯定也不一樣,復(fù)雜度也不一樣,例如有的想接入小程序的請求,我自己想接入客戶端里提供的數(shù)據(jù)請求方式等。我們這里只是通過實現(xiàn)一個簡單的jsonp適配器來講解下實現(xiàn)方式。
我們以 es6 的模塊方式來進行開發(fā)。所有的實現(xiàn)均在代碼中進行了講解。
// 這里的config是axios里所有的配置
const jsonpAdapter = (config) => {
return new Promise((resolve, reject) => {
// 是否已取消當(dāng)前操作
// 因jsonp沒有主動取消請求的方式
// 這里使用 isAbort 來標識
let isAbort = false;
// 定時器標識符
let timer = null;
// 執(zhí)行方法的名字,
const callbackName = `jsonp${Date.now()}_${Math.random()
.toString()
.slice(2)}`;
// 這里假設(shè)已經(jīng)實現(xiàn)了baseURL和url的拼接方法
const fullPath = buildFullPath(config.baseURL, config.url);
// 這里假設(shè)已經(jīng)實現(xiàn)了url和參數(shù)的拼接方法
// 不太一樣的地方在于,jsonp需要額外插入一個自己的回調(diào)方法
const url = buildURL(
fullPath,
{
...config.params,
...{ [config.jsonpCallback || 'callback']: callbackName },
},
config.paramsSerializer
);
// 創(chuàng)建一個script標簽
let script = document.createElement('script');
// 成功執(zhí)行操作后
function remove() {
if (script) {
script.onload = script.onerror = null;
// 移除script標簽
if (script.parentNode) {
script.parentNode.removeChild(script);
}
// 取消定時器
if (timer) {
clearTimeout(timer);
}
script = null;
}
}
// 成功請求后
window[callbackName] = (data) => {
// 若已需要請求,則不再執(zhí)行
if (isAbort) {
return;
}
// 返回的格式
const response = {
status: 200,
statusText: 'ok',
config,
request: script,
data: data,
};
remove();
// 實際上這里上一個settle操作,會額外判斷是否是合理的status狀態(tài)
// 若我們在config.validateStatus中設(shè)置404是合理的,也會進入到resolve狀態(tài)
// 但我們這里就不實現(xiàn)這個了
// settle(resolve, reject, response);
resolve(response);
};
// 請求失敗
script.onerror = function (error) {
remove();
reject(createError('Network Error', config, 404));
};
// 若設(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.在某種情況下使用自定義適配器,其他情況時還是使用 axios 自己的適配器。
第 1 種情況還好,只需要 return 自己適配器返回的結(jié)果結(jié)果即可;而第 2 種情況中,則有個小坑需要踩一下,我們這里也只講解下第 2 種情況。我要把剛才實現(xiàn)的 jsonp 適配器添加到 axios 中,并且只在參數(shù)有format=jsonp時才調(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án)
return defaultAxios({ ...config, ...{ adapter: undefined } });
},
});
使用自定義的適配器 jsonp 發(fā)起請求。
// 使用自定義的適配器jsonp發(fā)起請求
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 默認的適配器發(fā)起請求。
// 使用axios默認的適配器發(fā)起請求
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)實現(xiàn)了一個自定義適配器了,在滿足一定條件時可以觸發(fā)這個適配器。通過這個思路,我們也可以實現(xiàn)一個自定義的 mock 方法,例如當(dāng)參數(shù)中包含format=mock時則調(diào)用 mock 接口,否則就正常請求。
以上就是如何實現(xiàn)axios的自定義適配器adapter的詳細內(nèi)容,更多關(guān)于axios自定義適配器adapter的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
DD_belatedPNG,IE6下PNG透明解決方案(國外)
今天介紹DD_belatedPNG,只需要一個理由,就是它支持backgrond-position與background-repeat.這是其他js插件不具備的.2010-12-12
layui使用button按鈕 點擊出現(xiàn)彈層 彈層中加載表單的實例
今天小編就為大家分享一篇layui使用button按鈕 點擊出現(xiàn)彈層 彈層中加載表單的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09
uniapp實現(xiàn)全局變量的幾種方式總結(jié)
這里說全局變量,著重指的是能夠全局動態(tài)響應(yīng)的情況,下面這篇文章主要給大家介紹了關(guān)于uniapp實現(xiàn)全局變量的幾種方式,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-10-10
利用JQuery和JS實現(xiàn)奇偶行背景顏色自定義效果
本文將詳細介紹利用JQuery和JS實現(xiàn)奇偶行背景顏色自定義效果,需要的朋友可以參考下2012-11-11

