原文鏈接:https://www.cnblogs.com/libin-1/p/6853677.html
無論用JavaScript發(fā)送或獲取信息紫岩,我們都會用到Ajax总珠。Ajax不需要刷新頁面就能發(fā)送和獲取信息产雹,能使網(wǎng)頁實現(xiàn)異步更新。
幾年前浓恶,初始化Ajax一般使用jQuery的ajax方法:
$.ajax('some-url', {
????success:(data) => {/* do something with the data */ },
????error:(err) => {/* do something when an error happens */}
});
也可以不用jQuery紧憾,但不得不使用XMLHttpRequest,然而這是相當復雜
幸虧签夭,瀏覽器現(xiàn)在支持Fetch API齐邦,可以無須其他庫就能實現(xiàn)Ajax
瀏覽器支持
桌面瀏覽器
手機/平板電腦
所有主要的瀏覽器(除了Opera Mini和老的IE)都支持Fetch。針對不支持的第租,可以使用Fetch polyfill
Fetch獲取數(shù)據(jù)
使用Fetch獲取數(shù)據(jù)很容易措拇。只需要Fetch你想獲取資源。
假設(shè)我們想通過GitHub獲取一個倉庫慎宾,我們可以像下面這樣使用:
fetch('https://api.github.com/users/chriscoyier/repos');
Fetch會返回Promise丐吓,所以在獲取資源后,可以使用.then方法做你想做的璧诵。
fetch('https://api.github.com/users/chriscoyier/repos')?
????.then(response => {/* do something */})
如果這是你第一次遇見Fetch汰蜘,也許驚訝于Fetch返回的response。如果console.log返回的response之宿,會得到下列信息:
{
????body: ReadableStream?
????bodyUsed:false? headers: Headers?
????ok :true?
????redirected :false?
????status :200?
????statusText :"OK"?
????type :"cors"?
????url :"http://some-website.com/some-url"
? __proto__ : Response
}
可以看出Fetch返回的響應能告知請求的狀態(tài)族操。從上面例子看出請求是成功的(ok是true,status是200),但是我們想獲取的倉庫名卻不在這里色难。
顯然泼舱,我們從GitHub請求的資源都存儲在body中,作為一種可讀的流枷莉。所以需要調(diào)用一個恰當方法將可讀流轉(zhuǎn)換為我們可以使用的數(shù)據(jù)娇昙。
Github返回的響應是JSON格式的,所以調(diào)用response.json方法來轉(zhuǎn)換數(shù)據(jù)笤妙。
還有其他方法來處理不同類型的響應冒掌。如果請求一個XML格式文件,則調(diào)用response.text蹲盘。如果請求圖片股毫,使用response.blob方法。
所有這些方法(response.json等等)返回另一個Promise召衔,所以可以調(diào)用.then方法處理我們轉(zhuǎn)換后的數(shù)據(jù)铃诬。
fetch('https://api.github.com/users/chriscoyier/repos')?
????.then(response => response.json())?
????.then(data => {
????????// data就是我們請求的reposconsole.log(data)
? });
可以看出Fetch獲取數(shù)據(jù)方法簡短并且簡單。
接下來苍凛,讓我們看看如何使用Fetch發(fā)送數(shù)據(jù)趣席。
Fetch發(fā)送數(shù)據(jù)
使用Fetch發(fā)送也很簡單,只需要配置三個參數(shù)醇蝴。
fetch('some-url', options);
第一個參數(shù)是設(shè)置請求方法(如post宣肚、put或del),F(xiàn)etch會自動設(shè)置方法為get哑蔫。
第二個參數(shù)是設(shè)置頭部钉寝。因為一般使用JSON數(shù)據(jù)格式弧呐,所以設(shè)置ContentType為application/json闸迷。
第三個參數(shù)是設(shè)置包含JSON內(nèi)容的主體。因為JSON內(nèi)容是必須的俘枫,所以當設(shè)置主體時會調(diào)用JSON.stringify腥沽。
實踐中,post請求會像下面這樣:
let content = {some:'content'};
// The actual
fetch requestfetch('some-url', {
????method:'post',
????headers: {
????????'Content-Type':'application/json'?
????},
????body:JSON.stringify(content)
})
// .then()...
Fetch處理異常
雖然希望Ajax響應成功鸠蚪,但是仍會有問題出現(xiàn):
可能嘗試獲取不存在的資源
沒有權(quán)限獲取資源
輸入?yún)?shù)有誤
服務器拋出異常
服務器超時
服務器崩潰
API更改
...
假設(shè)我們試圖獲取不存在錯誤今阳,并了解如何處理錯誤。下面的例子我將chriscoyier拼錯為chrissycoyier
// 獲取chrissycoyier's repos 而不是 chriscoyier's reposfetch('https://api.github.com/users/chrissycoyier/repos')
為了處理此錯誤茅信,我們需要使用catch方法盾舌。
也許我們會用下面這種方法:
fetch('https://api.github.com/users/chrissycoyier/repos')?
????.then(response => response.json())?
????.then(data =>console.log('data is', data))?
.catch(error =>console.log('error is', error));
然而卻得到下面這樣結(jié)果:
獲取失敗,但是第二個.then方法會執(zhí)行蘸鲸。
如果console.log此次響應妖谴,會看出不同:
{
????body: ReadableStream?
????bodyUsed:true?
????headers: Headers?
????ok:false????// Response is not ok?
????redirected:false?
????status:404// HTTP status is 404.
? ? statusText:"Not Found"http:// Request not found?
????type:"cors"?
????url:"https://api.github.com/users/chrissycoyier/repos"
}
大部分是一樣的,只有ok、status和statusText是不同的膝舅,正如所料嗡载,GitHub上沒有發(fā)現(xiàn)chrissycoyier。
上面響應告訴我們Fetch不會關(guān)心AJAX是否成功仍稀,他只關(guān)心從服務器發(fā)送請求和接收響應洼滚,如果響應失敗我們需要拋出異常。
因此技潘,初始的then方法需要被重寫遥巴,以至于如果響應成功會調(diào)用response.json。最簡單方法是檢查response是否為ok享幽。
fetch('some-url')?
????.then(response => {
????????if (response.ok) {
????????????return response.json()? ?
????????}else {
????????????// Find some way to get to execute .catch()
? ? ? ?}
? });
一旦我們知道請求是不成功的挪哄,我可以throw異常或rejectPromise來調(diào)用catch琉闪。
// throwing an Error
else {
????thrownewError('something went wrong!')
}
// rejecting a Promise
else {
????returnPromise.reject('something went wrong!')
}
這里選擇Promise.reject迹炼,是因為容易擴展。拋出異常方法也不錯颠毙,但是無法擴展斯入,唯一益處在于便于棧跟蹤。
所以蛀蜜,到現(xiàn)在代碼應該是這樣的:
fetch('https://api.github.com/users/chrissycoyier/repos')?
????.then(response => {
????????if (response.ok) {
????????????return response.json()? ?
????????}else {
????????????returnPromise.reject('something went wrong!')? ?
????????}?
})?
.then(data =>console.log('data is', data))?
.catch(error =>console.log('error is', error));
這樣錯誤就會進入catch語句中刻两。
但是rejectPromise時,只輸出字符串不太好滴某。這樣不清楚哪里出錯了磅摹,你肯定也不會想在異常時,輸出下面這樣:
讓我們在看看響應:
在這個例子中霎奢,我們知道資源是不存在户誓。所以我們可以返回404狀態(tài)或Not Found原因短語,然而我們就知道如何處理幕侠。
為了在.catch中獲取status或statusText帝美,我們可以reject一個JavaScript對象:
上面的錯誤處理方法對于下面這些不需要解釋的HTTP狀態(tài)很適用。
401: Unauthorized
404: Not found
408: Connection timeout
...
但對于下面這些特定的錯誤不適用:
400:Bad request
例如晤硕,如果請求錯誤缺少必要的參數(shù)悼潭,就會返回400.
光在catch中告訴狀態(tài)及原因短語并不足夠。我們需要知道缺少什么參數(shù)舞箍。
所以服務器需要返回一個對象舰褪,告訴造成錯誤請求原因。如果使用Node和Express疏橄,會返回像下面這樣的響應:
res.status(400).send({
????err:'no first name'
})
無法在最初的.then方法中reject占拍,因為錯誤對象需要response.json來解析。
解決的方法是需要兩個then方法。這樣可以首先通過response.json讀取刷喜,然后決定怎么處理残制。
首先我們調(diào)用response.json讀取服務器發(fā)來的JSON數(shù)據(jù),response.json返回Promise掖疮,所以可以鏈式調(diào)用.then方法初茶。
在第一個.then中調(diào)用第二個.then,因為我們?nèi)韵Mㄟ^repsonse.ok判斷響應是否成功浊闪。
如果想發(fā)送狀態(tài)和原因短語恼布,可以使用Object.assign()將二者結(jié)合為一個對象。
可以使用這樣新的handleResponse函數(shù)搁宾,讓數(shù)據(jù)能自動的進入.then和.catch中折汞。
處理其他響應類型
到現(xiàn)在,我們只處理JSON格式的響應盖腿,而返回JSON格式數(shù)據(jù)大約占90%爽待。
至于其他的10%呢?
假設(shè)上面的例子返回的是XML格式的響應翩腐,也許會收到下面異常:
這是因為XML格式不是JSON格式鸟款,我們無法使用response.json,事實上茂卦,我們需要response.text何什,所以我們需要通過判斷響應的頭部來決定內(nèi)容格式:
當我遇見這種問題時,我嘗試使用ExpressJWT處理身份驗證等龙,我不知道可以發(fā)生JSON響應數(shù)據(jù)处渣,所以我將XML格式設(shè)為默認。
這是我們到現(xiàn)在完整代碼:
介紹zlFetch
zlFetch庫就是上例中handleResponse函數(shù)蛛砰,所以可以不用生成此函數(shù)罐栈,不需要擔心響應來處理數(shù)據(jù)和錯誤。
典型的zlfetch像下面這樣:
使用之前暴备,需要安裝zlFetch
npm install zl-fetch --save
接著悠瞬,需要引入到你的代碼中们豌,如果你需要polyfill涯捻,確保加入zlFetch之前引入它。
zlFetch還能無須轉(zhuǎn)換成JSON格式就能發(fā)送JSON數(shù)據(jù)望迎。
下面兩個函數(shù)做了同樣事情障癌,zlFetch加入Content-type然后將內(nèi)容轉(zhuǎn)換為JSON格式。
zlFetch處理身份認證也很容易辩尊。
常用方法是在頭部加入Authorization涛浙,其值設(shè)為Bearer your-token-here。如果你需要增加token選項,zlFetch會幫你創(chuàng)建此域轿亮。
所以疮薇,下面兩種代碼是一樣的:
下面就是使用zlFetch來從GitHub上獲取repos:
總結(jié)
Fetch是很好的方法,能發(fā)送和接收數(shù)據(jù)我注。不需要在編寫XHR請求或依賴于jQuery按咒。
盡管Fetch很好,但是其錯誤處理不是很直接但骨。在處理之前励七,需要讓錯誤信息進入到catch方法中。
使用zlFetch庫奔缠,就不需要擔心錯誤處理了掠抬。