前端工程師使用fetch常見的四種錯誤(翻譯)

查看原文  javascript

fetch 是瀏覽器中發送http請求最流行方式。它不只僅是一個比XMLHttpRequest更好、更符合人體工程學的API,它還帶來了許多使人興奮的新功能,好比響應流、對憑證和CORS請求的更多控制,以及與ServiceWorkers和緩存API的集成java

我本身也在研究並使用它,同時也見證了fetch的成長(原文:普遍使用), 發現即便有經驗的開發者也會犯一些比較常見的錯誤。我認爲這在很大程度上與fetch的API在表面上看起來它的行爲模式都很像jQuery $.ajax、angularJS的$http axios等。它們之間有一些重要的區別,而這些區別大多源於fetch做爲底層網絡請求的原型設計jquery

1.認爲一旦發生http錯誤,promise 就會 被 reject

Fetch是基於promise的,所以比較容易想到,若是服務器返回http的錯誤狀態碼,像404或是500,Promise就會被拒絕,就像原始的XMLHttpRequest同樣;但fetch並非這樣的——它僅僅在發生「network error(網絡錯誤)」纔會被拒絕(這是一個使人困惑的術語,但規範是這麼說的)。若是能夠服務器得到http錯誤狀態,則代表服務器正常工做且在處理請求,而「network error(網絡錯誤)」表示根本沒法到達服務器(例如鏈接拒絕或名稱未解析)或請求配置有錯誤(錯誤的請求地址)。
ios

例如, ftp 協議 並不支持fetch,因此它會返回一個被rejected 的 promise :git

fetch('ftp://example.com')  .catch(err => console.error('Caught error: ', err))複製代碼

可是請求一個返回404錯誤的URL並有沒有發生錯誤(即相似404這類錯誤fetch返回的並非rejected)angularjs

fetch('https://jsonplaceholder.typicode.com/404') 
 .then(res => console.log('response: ', res))  .catch(console.error)複製代碼

我想對大部分開發者來講,http返回的錯誤對web應用程序處理一個rejected的promise是很是有用的。爲此(fetch對http錯誤的處理方式),咱們僅僅須要對驗證 ok  屬性的值 ,若是  okes6

的值爲false ,則 rejectgithub

fetch('https://jsonplaceholder.typicode.com/404')  
    .then(res => {    
        if(res.ok) {      
             return res;    
        } else {      
             throw Error(`Request rejected with status ${res.status}`);   
     }})  
    .catch(console.error)複製代碼


2.忘記包含credentials

與XHR不一樣,fetch在默認狀況下不包括請求中的任何cookie。所以,若是針對您的API的請求須要基於cookie的受權(大多數web應用程序都須要),那麼必須有這個選項,不然您的調用可能會返回401未受權。
web

這很是簡單:ajax

fetch('/url', { credentials: 'include' })複製代碼

注意,若是您正在使用fetch polyfill,那麼(不幸的是)您可能會偶爾地忘記這一點,一切看起來就好像你已經正在使用credentials: 'include'同樣,這是由於它只是在底層使用XHR,而XHR自動具備這種行爲。

3.上傳JSON時忘記設置`Content-Type` to `application/json` 

$http, axios和其它http工具在默認狀況下會爲您設置此頭,所以很容易忘記。

fetch('https://cameronnokes.com/slack-ron-swanson-quote-bot/ron', {
   method: 'POST',   
   body: JSON.stringify({text: 'bacon'}),  
   headers: {'Content-Type': 'application/json'}})
   .then(res => res.json())
   .then(console.log)複製代碼

若是不包含該頭,服務器可能會返回一個400Bad Request ,由於endpoint不支持純文本內容類型,或者沒法處理這種文本的body,這取決於API的實現。但不管如何,您的API可能會返回HTTP錯誤狀態。

4.使用字符串手動拼接一個複雜的查詢參數

我常常看到相似這樣的代碼:

fetch('/endpoint?foo=${n > 1 ? 123 : 456}&bar=abc&query=${encodeURIComponent(someString || '')}複製代碼

這並不徹底是糟糕的,但我認爲若是咱們讓新的URLSearchParams類和少許lodash爲咱們作更多的工做,它會更容易閱讀,更少出錯。


URLSearchParams類很是棒,能夠正確地處理編碼、將對象鏈接到字符串等等。我認爲這樣更容易閱讀和維護。

總結:

Fetch很棒,可是要負責任地去享受


======================= 如下爲譯者添加=========================

伴隨fetch的問世及發展,目前在生產環境使用fetch的企業愈來愈多。開源社區上有關fetch polyfilll已經多款供選,又因其基於Promise,如下能夠完美的解決其兼容性問題(特別是IE),僅參考:

  • 因爲 IE8 是 ES3,須要引入 ES5polyfill: es5-shim, es5-sham

  • 引入 Promisepolyfill: es6-promise

  • 引入 fetch 探測庫:fetch-detector

  • 引入 fetchpolyfill: fetch-ie8

  • 可選:若是你還使用了 jsonp,引入 fetch-jsonp

  • 可選:開啓 Babelruntime 模式,如今就使用 async/await

fetch 的好處無需多語,但fetch也存在一些問題,例如不支持timeout,不支持 progress,特別是fetch在跨域問題上與傳統跨域的處理方式的區別,社區中都對應的解決方案,此文不一一列舉。正如原諒做者所言:負責任地去享受

相關文章
相關標籤/搜索