基於原生fetch封裝一個帶有攔截器功能的fetch,相似axios的攔截器

背景

爲何要給fetch增長攔截器?瀏覽器提供的fetch方法不夠用嗎?ios

是的。原生fetch確實不夠用。程序員

在項目中,若是想要在全部的網絡請求以前往header中加入權限信息(好比:authorization=xxxx)。用原生fetch的話,你只能在每一個fetch請求的時候,header配置中寫上authorization=xxxx。或者在請求結果返回來以後,對響應結果作一些特殊處理,在原生fetch中,咱們只能在每一個請求的結果回來以後,都寫一遍特殊處理。這樣作起來一點都不優雅,一點都不高級。相信每個程序員都是一個懶人,能用少許代碼實現出更優雅,更健壯程序的話,絕對不用大量代碼實現一個很脆弱的程序。ajax

用過axios的人,都知道axios有攔截器功能(axios.interceptors)。遇到須要在全部ajax請求以前或者請求完成以後作一些事情的話,咱們就能夠在攔截器中寫。攔截器能夠在發起請求以前攔截請求,對請求作一些處理,而後,再繼續請求;也能夠在請求完成以後攔截請求,對響應結果作一些處理,而後再把結果返回。axios

這樣作的好處是咱們不須要在每一個請求的時候,都去寫相同的這些代碼,能夠把全局生效/通用的處理放在攔截器中來實現。能夠省去不少代碼,也避免了咱們在開發時,不當心某一個請求漏掉了作全局處理。api

攔截器設計思考

  1. 目的是在原生fetch基礎之上增長攔截請求的功能。
  2. 封裝後,最終暴露在外面的接口應該於原生fetch的使用方法一致,同時增長攔截器功能
  3. 攔截器的API設計參考axios的攔截器(我的比較喜歡axios的攔截器設計)。

攔截器API使用方法

你能夠在then和catch以前攔截請求和響應。promise

// 添加一個請求攔截器
c_fetch.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  });

// 添加一個響應攔截器
c_fetch.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  });
複製代碼

攔截器實現方案

  1. 爲了保證封裝後的fetch暴露在最外面的api跟原生fetch保持一致。因此,咱們要暴露到最外面的api應該是個跟fetch接收相同參數的函數。
    咱們看下原生fetch方法語法解釋和接收參數解釋。
語法: 
Promise<Response> fetch(input[, init]); 
複製代碼
參數:
?input
定義要獲取的資源。這多是:
一個 USVString 字符串,包含要獲取資源的 URL。一些瀏覽器會接受 blob: 和 data: 做爲 schemes.
一個 Request 對象。

init 可選
一個配置項對象,包括全部對請求的設置。可選的參數有:
method: 請求使用的方法,如 GET、POST。
headers: 請求的頭信息,形式爲 Headers 的對象或包含 ByteString 值的對象字面量。
body: 請求的 body 信息:多是一個 Blob、BufferSource、FormData、URLSearchParams 或者 USVString 對象。注意 GET 或 HEAD 方法的請求不能包含 body 信息。
mode: 請求的模式,如 cors、 no-cors 或者 same-origin。
credentials: 請求的 credentials,如 omit、same-origin 或者 include。爲了在當前域名內自動發送 cookie , 必須提供這個選項, 從 Chrome 50 開始, 這個屬性也能夠接受 FederatedCredential 實例或是一個 PasswordCredential 實例。
cache:  請求的 cache 模式: default 、 no-store 、 reload 、 no-cache 、 force-cache 或者 only-if-cached 。
redirect: 可用的 redirect 模式: follow (自動重定向), error (若是產生重定向將自動終止而且拋出一個錯誤), 或者 manual (手動處理重定向). 在Chrome中,Chrome 47以前的默認值是 follow,從 Chrome 47開始是 manual。
referrer: 一個 USVString 能夠是 no-referrer、client或一個 URL。默認是 client。
referrerPolicy: Specifies the value of the referer HTTP header. May be one of no-referrer、 no-referrer-when-downgrade、 origin、 origin-when-cross-origin、 unsafe-url 。
integrity: 包括請求的  subresource integrity 值 ( 例如: sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=)。
複製代碼

原生fetch的詳細文檔請移步MDN查閱:
developer.mozilla.org/zh-CN/docs/…瀏覽器

從上面的fetch的語法解釋和參數解釋,咱們能夠知道fetch是個函數;接收兩個參數,第一個參數定義要獲取的資源,第二個參數爲可選項,一個配置項對象,包括全部對請求的設置;fetch函數分返回結果是一個promise對象。cookie

  1. 咱們先來實現封裝後,暴露出來的c_fetch函數。
function c_fetch (input, init = {}) {
    //fetch默認請求方式設爲GET
    if(!init.method){
      init.method = 'GET'
    }
    
    //interceptors_req是攔截請求的攔截處理函數集合
    //後面會講解interceptors_req的定義與實現
    interceptors_req.forEach(interceptors => {
      init = interceptors(init);
    })
    
    //在原生fetch外面封裝一個promise,爲了在promise裏面能夠對fetch請求的結果作攔截處理。
    //同時,保證c_fetch函數返回的結果是個promise對象。
    return new Promise(function (resolve, reject) {
      //發起fetch請求,fetch請求的形參是接收上層函數的形參
      fetch(input, init).then(res => {
        //interceptors_res是攔截響應結果的攔截處理函數集合
        //後面會講解interceptors_res的定義與實現
        interceptors_res.forEach(interceptors => {
          //攔截器對響應結果作處理,把處理後的結果返回給響應結果。
          res = interceptors(res);
        })
        //將攔截器處理後的響應結果resolve出去
        resolve(res)
      }).catch(err => {
        reject(err);
      })
    })

  }
複製代碼
  1. c_fetch 函數實現完了,咱們就差攔截器的實現了。咱們在 c_fetch 函數的基礎上增長interceptors,用來註冊攔截器。
//定義用來存儲攔截請求和攔截響應結果的處理函數集合
  let interceptors_req = [], interceptors_res = [];

  function c_fetch (input, init = {}) {
    //fetch默認請求方式設爲GET
    if(!init.method){
      init.method = 'GET'
    }
    
    //interceptors_req是攔截請求的攔截處理函數集合
    interceptors_req.forEach(interceptors => {
      init = interceptors(init);
    })
    
    //在原生fetch外面封裝一個promise,爲了在promise裏面能夠對fetch請求的結果作攔截處理。
    //同時,保證c_fetch函數返回的結果是個promise對象。
    return new Promise(function (resolve, reject) {
      //發起fetch請求,fetch請求的形參是接收上層函數的形參
      fetch(input, init).then(res => {
        //interceptors_res是攔截響應結果的攔截處理函數集合
        interceptors_res.forEach(interceptors => {
          //攔截器對響應結果作處理,把處理後的結果返回給響應結果。
          res = interceptors(res);
        })
        //將攔截器處理後的響應結果resolve出去
        resolve(res)
      }).catch(err => {
        reject(err);
      })
    })

  }
  
  //在c_fetch函數上面增長攔截器interceptors,攔截器提供request和response兩種攔截器功能。
  //能夠經過request和response的use方法來綁定兩種攔截器的處理函數。
  //use方法接收一個參數,參數爲一個callback函數,callback函數用來做爲攔截器的處理函數;
  //request.use方法會把callback放在interceptors_req中,等待執行。
  //response.use方法會把callback放在interceptors_res中,等待執行。
  //攔截器的處理函數callback接收一個參數。
  //request攔截器的callback接收的是請求發起前的config;
  //response攔截器的callback接收的是網絡請求的response結果。
  c_fetch.interceptors = {
    request: {
      use: function (callback) {
        interceptors_req.push(callback);
      }
    },
    response: {
      use: function (callback) {
        interceptors_res.push(callback);
      }
    }
  }
複製代碼
  1. 最後將整個封裝以後,含有攔截器功能的fetch包裝爲一個插件暴露給開發者使用。
/** * c_fetch * 基於原生fetch封裝了攔截器功能,暴露出來的c_fetch跟原生fetch用法一致,只是增長了攔截器功能。攔截器用法參考axios的攔截器用法。 * 攔截器: c_fetch.interceptors * 注意: 攔截器不攔截reject類型的response結果 */

  //定義用來存儲攔截請求和攔截響應結果的處理函數集合
  let interceptors_req = [], interceptors_res = [];

  function c_fetch (input, init = {}) {
    //fetch默認請求方式設爲GET
    if(!init.method){
      init.method = 'GET'
    }
    
    //interceptors_req是攔截請求的攔截處理函數集合
    interceptors_req.forEach(interceptors => {
      init = interceptors(init);
    })
    
    //在原生fetch外面封裝一個promise,爲了在promise裏面能夠對fetch請求的結果作攔截處理。
    //同時,保證c_fetch函數返回的結果是個promise對象。
    return new Promise(function (resolve, reject) {
      //發起fetch請求,fetch請求的形參是接收上層函數的形參
      fetch(input, init).then(res => {
        //interceptors_res是攔截響應結果的攔截處理函數集合
        interceptors_res.forEach(interceptors => {
          //攔截器對響應結果作處理,把處理後的結果返回給響應結果。
          res = interceptors(res);
        })
        //將攔截器處理後的響應結果resolve出去
        resolve(res)
      }).catch(err => {
        reject(err);
      })
    })

  }
  
  //在c_fetch函數上面增長攔截器interceptors,攔截器提供request和response兩種攔截器功能。
  //能夠經過request和response的use方法來綁定兩種攔截器的處理函數。
  //use方法接收一個參數,參數爲一個callback函數,callback函數用來做爲攔截器的處理函數;
  //request.use方法會把callback放在interceptors_req中,等待執行。
  //response.use方法會把callback放在interceptors_res中,等待執行。
  //攔截器的處理函數callback接收一個參數。
  //request攔截器的callback接收的是請求發起前的config;
  //response攔截器的callback接收的是網絡請求的response結果。
  c_fetch.interceptors = {
    request: {
      use: function (callback) {
        interceptors_req.push(callback);
      }
    },
    response: {
      use: function (callback) {
        interceptors_res.push(callback);
      }
    }
  }

  export default c_fetch;
複製代碼

總結

本篇文章只是實現了一個最基本的攔截器功能,文章字數有限,沒有深刻講解更加成熟的攔截器實現方式。有興趣的朋友能夠閱讀下axios的攔截器實現,頗有意思的。axios的攔截器會更加完善。網絡

我這裏針對fetch的封裝也只是最基本的封裝,目的是講解攔截器的實現,沒有過於複雜化。上文中暴露出來的c_fetch其實能夠在封裝一層cc_fetch,用bind方法把c_fetch的方法綁定在cc_fetch上,最後暴露出來的是cc_fetch。這樣作的好處是保護了c_fetch的方法不會被外部所影響,篡改等。固然了,這只是我我的的一些見解,不表明全部人。cors

最後謝謝各位可以堅持閱讀到最後,但願您閱讀本篇文章可以有所收穫。謝謝🙏~

相關文章
相關標籤/搜索