axios拦截器的实现
攔截器設(shè)計(jì)與實(shí)現(xiàn)
#需求分析
我們希望能對(duì)請(qǐng)求的發(fā)送和響應(yīng)做攔截,也就是在發(fā)送請(qǐng)求之前和接收到響應(yīng)之后做一些額外邏輯。
我們希望設(shè)計(jì)的攔截器的使用方式如下:
// 添加一個(gè)請(qǐng)求攔截器 axios.interceptors.request.use(function (config) { // 在發(fā)送請(qǐng)求之前可以做一些事情 return config; }, function (error) { // 處理請(qǐng)求錯(cuò)誤 return Promise.reject(error); }); // 添加一個(gè)響應(yīng)攔截器 axios.interceptors.response.use(function (response) { // 處理響應(yīng)數(shù)據(jù) return response; }, function (error) { // 處理響應(yīng)錯(cuò)誤 return Promise.reject(error); });在?axios?對(duì)象上有一個(gè)?interceptors?對(duì)象屬性,該屬性又有?request?和?response?2 個(gè)屬性,它們都有一個(gè)?use?方法,use?方法支持 2 個(gè)參數(shù),第一個(gè)參數(shù)類(lèi)似 Promise 的?resolve?函數(shù),第二個(gè)參數(shù)類(lèi)似 Promise 的?reject?函數(shù)。我們可以在?resolve?函數(shù)和?reject?函數(shù)中執(zhí)行同步代碼或者是異步代碼邏輯。
并且我們是可以添加多個(gè)攔截器的,攔截器的執(zhí)行順序是鏈?zhǔn)揭来螆?zhí)行的方式。對(duì)于?request?攔截器,后添加的攔截器會(huì)在請(qǐng)求前的過(guò)程中先執(zhí)行;對(duì)于?response?攔截器,先添加的攔截器會(huì)在響應(yīng)后先執(zhí)行。
axios.interceptors.request.use(config => { config.headers.test += '1' return config }) axios.interceptors.request.use(config => { config.headers.test += '2' return config })此外,我們也可以支持刪除某個(gè)攔截器,如下:
const myInterceptor = axios.interceptors.request.use(function () {/*...*/}) axios.interceptors.request.eject(myInterceptor)#整體設(shè)計(jì)
我們先用一張圖來(lái)展示一下攔截器工作流程:
整個(gè)過(guò)程是一個(gè)鏈?zhǔn)秸{(diào)用的方式,并且每個(gè)攔截器都可以支持同步和異步處理,我們自然而然地就聯(lián)想到使用 Promise 鏈的方式來(lái)實(shí)現(xiàn)整個(gè)調(diào)用過(guò)程。
在這個(gè) Promise 鏈的執(zhí)行過(guò)程中,請(qǐng)求攔截器?resolve?函數(shù)處理的是?config?對(duì)象,而相應(yīng)攔截器?resolve?函數(shù)處理的是?response?對(duì)象。
在了解了攔截器工作流程后,我們先要?jiǎng)?chuàng)建一個(gè)攔截器管理類(lèi),允許我們?nèi)ヌ砑?刪除和遍歷攔截器。
#攔截器管理類(lèi)實(shí)現(xiàn)
根據(jù)需求,axios?擁有一個(gè)?interceptors?對(duì)象屬性,該屬性又有?request?和?response?2 個(gè)屬性,它們對(duì)外提供一個(gè)?use?方法來(lái)添加攔截器,我們可以把這倆屬性看做是一個(gè)攔截器管理對(duì)象。use?方法支持 2 個(gè)參數(shù),第一個(gè)是?resolve?函數(shù),第二個(gè)是?reject?函數(shù),對(duì)于?resolve?函數(shù)的參數(shù),請(qǐng)求攔截器是?AxiosRequestConfig?類(lèi)型的,而響應(yīng)攔截器是?AxiosResponse?類(lèi)型的;而對(duì)于?reject?函數(shù)的參數(shù)類(lèi)型則是?any?類(lèi)型的。
根據(jù)上述分析,我們先來(lái)定義一下攔截器管理對(duì)象對(duì)外的接口。
#接口定義
types/index.ts:
export interface AxiosInterceptorManager<T> { use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number eject(id: number): void } export interface ResolvedFn<T=any> { (val: T): T | Promise<T> } export interface RejectedFn { (error: any): any }這里我們定義了?AxiosInterceptorManager?泛型接口,因?yàn)閷?duì)于?resolve?函數(shù)的參數(shù),請(qǐng)求攔截器和響應(yīng)攔截器是不同的。
#代碼實(shí)現(xiàn)
import { ResolvedFn, RejectedFn } from '../types' interface Interceptor<T> { resolved: ResolvedFn<T> rejected?: RejectedFn } export default class InterceptorManager<T> { private interceptors: Array<Interceptor<T> | null> constructor() { this.interceptors = [] } use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number { this.interceptors.push({ resolved, rejected }) return this.interceptors.length - 1 } forEach(fn: (interceptor: Interceptor<T>) => void): void { this.interceptors.forEach(interceptor => { if (interceptor !== null) { fn(interceptor) } }) } eject(id: number): void { if (this.interceptors[id]) { this.interceptors[id] = null } } }我們定義了一個(gè)?InterceptorManager?泛型類(lèi),內(nèi)部維護(hù)了一個(gè)私有屬性?interceptors,它是一個(gè)數(shù)組,用來(lái)存儲(chǔ)攔截器。該類(lèi)還對(duì)外提供了 3 個(gè)方法,其中?use?接口就是添加攔截器到?interceptors中,并返回一個(gè)?id?用于刪除;forEach?接口就是遍歷?interceptors?用的,它支持傳入一個(gè)函數(shù),遍歷過(guò)程中會(huì)調(diào)用該函數(shù),并把每一個(gè)?interceptor?作為該函數(shù)的參數(shù)傳入;eject?就是刪除一個(gè)攔截器,通過(guò)傳入攔截器的?id?刪除。
#鏈?zhǔn)秸{(diào)用實(shí)現(xiàn)
本小節(jié)需要你對(duì) Promise 掌握和理解,可以前往?mdn?學(xué)習(xí)。
當(dāng)我們實(shí)現(xiàn)好攔截器管理類(lèi),接下來(lái)就是在?Axios?中定義一個(gè)?interceptors?屬性,它的類(lèi)型如下:
interface Interceptors {request: InterceptorManager<AxiosRequestConfig> response: InterceptorManager<AxiosResponse> } export default class Axios { interceptors: Interceptors constructor() { this.interceptors = { request: new InterceptorManager<AxiosRequestConfig>(), response: new InterceptorManager<AxiosResponse>() } } }Interceptors?類(lèi)型擁有 2 個(gè)屬性,一個(gè)請(qǐng)求攔截器管理類(lèi)實(shí)例,一個(gè)是響應(yīng)攔截器管理類(lèi)實(shí)例。我們?cè)趯?shí)例化?Axios?類(lèi)的時(shí)候,在它的構(gòu)造器去初始化這個(gè)?interceptors?實(shí)例屬性。
接下來(lái),我們修改?request?方法的邏輯,添加攔截器鏈?zhǔn)秸{(diào)用的邏輯:
core/Axios.ts:
interface PromiseChain {resolved: ResolvedFn | ((config: AxiosRequestConfig) => AxiosPromise) rejected?: RejectedFn } request(url: any, config?: any): AxiosPromise { if (typeof url === 'string') { if (!config) { config = {} } config.url = url } else { config = url } const chain: PromiseChain[] = [{ resolved: dispatchRequest, rejected: undefined }] this.interceptors.request.forEach(interceptor => { chain.unshift(interceptor) }) this.interceptors.response.forEach(interceptor => { chain.push(interceptor) }) let promise = Promise.resolve(config) while (chain.length) { const { resolved, rejected } = chain.shift()! promise = promise.then(resolved, rejected) } return promise }首先,構(gòu)造一個(gè)?PromiseChain?類(lèi)型的數(shù)組?chain,并把?dispatchRequest?函數(shù)賦值給?resolved?屬性;接著先遍歷請(qǐng)求攔截器插入到?chain?的前面;然后再遍歷響應(yīng)攔截器插入到?chain?后面。
接下來(lái)定義一個(gè)已經(jīng) resolve 的?promise,循環(huán)這個(gè)?chain,拿到每個(gè)攔截器對(duì)象,把它們的?resolved?函數(shù)和?rejected?函數(shù)添加到?promise.then?的參數(shù)中,這樣就相當(dāng)于通過(guò) Promise 的鏈?zhǔn)秸{(diào)用方式,實(shí)現(xiàn)了攔截器一層層的鏈?zhǔn)秸{(diào)用的效果。
注意我們攔截器的執(zhí)行順序,對(duì)于請(qǐng)求攔截器,先執(zhí)行后添加的,再執(zhí)行先添加的;而對(duì)于響應(yīng)攔截器,先執(zhí)行先添加的,后執(zhí)行后添加的。
#demo 編寫(xiě)
在?examples?目錄下創(chuàng)建?interceptor?目錄,在?interceptor?目錄下創(chuàng)建?index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Interceptor example</title> </head> <body> <script src="/__build__/interceptor.js"></script> </body> </html>接著創(chuàng)建?app.ts?作為入口文件:
import axios from '../../src/index'axios.interceptors.request.use(config => { config.headers.test += '1' return config }) axios.interceptors.request.use(config => { config.headers.test += '2' return config }) axios.interceptors.request.use(config => { config.headers.test += '3' return config }) axios.interceptors.response.use(res => { res.data += '1' return res }) let interceptor = axios.interceptors.response.use(res => { res.data += '2' return res }) axios.interceptors.response.use(res => { res.data += '3' return res }) axios.interceptors.response.eject(interceptor) axios({ url: '/interceptor/get', method: 'get', headers: { test: '' } }).then((res) => { console.log(res.data) })該 demo 我們添加了 3 個(gè)請(qǐng)求攔截器,添加了 3 個(gè)響應(yīng)攔截器并刪除了第二個(gè)。運(yùn)行該 demo 我們通過(guò)瀏覽器訪(fǎng)問(wèn),我們發(fā)送的請(qǐng)求添加了一個(gè)?test?的請(qǐng)求 header,它的值是?321;我們的響應(yīng)數(shù)據(jù)返回的是?hello,經(jīng)過(guò)響應(yīng)攔截器的處理,最終我們輸出的數(shù)據(jù)是?hello13。
至此,我們給?ts-axios?實(shí)現(xiàn)了攔截器功能,它是一個(gè)非常實(shí)用的功能,在實(shí)際工作中我們可以利用它做一些需求如登錄權(quán)限認(rèn)證。
我們目前通過(guò)?axios?發(fā)送請(qǐng)求,往往會(huì)傳入一堆配置,但是我們也希望?ts-axios?本身也會(huì)有一些默認(rèn)配置,我們把用戶(hù)傳入的自定義配置和默認(rèn)配置做一層合并。其實(shí),大部分的 JS 庫(kù)都是類(lèi)似的玩法。下面一章我們就來(lái)實(shí)現(xiàn)這個(gè) feature。
轉(zhuǎn)載于:https://www.cnblogs.com/QianDingwei/p/11403923.html
總結(jié)
以上是生活随笔為你收集整理的axios拦截器的实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: axios取消功能的设计与实现
- 下一篇: 浅谈微信小程序生命周期