使用 fetch 來進行 Ajax 呼叫

前言

在很久很久很久很久以前 ( 好啦,其實也沒多久… ),那時 Ajax 正慢慢的紅起來,而如果大家剛好有經過那個時期,大概就會知道 XMLHttpRequest 這個東西,而如果那個時代又真的下去寫後,就會發現,阿,真是 OOXX 的有夠難寫….
不過,大家也知道,這錯不是錯在 XMLHttpRequest 上,而是時代的混亂阿!! ( 其實現在也沒好到哪去…)

而隨著時代的進步,我們開始擁有了 jQuery ,jQuery 幫我們把 XMLHttpRequest 封裝的好好的,並且解決了各種不同瀏覽器的判斷問題,讓我們快速的進入了新的網頁時代!!那時候看到 jQuery ,真的覺得,阿,他是神物阿!!!

後來,又出現了 KO ( ASP.NET MVC 4 的時代 ),也依舊覺得他是神物….然後直到 Google 家的 NG ( AngularJS ),更又覺得他是神物中的神物,而 AngularJS 更是把一切要用的東西全部都封裝起來了,我們不用再使用 jQuery ( 雖然 NG 裡面也是包 jQuery Lite ),到這個階段時期,我們使用 AngularJS 後,我們幾乎就可以不用管最底層的 XMLHttpRequest ,也不用管在往上的 jQuery 。

但又隨著時代的進步…. ( 其實說真的,也才幾年的時間… ) Facebook 的 React 大舉入侵,但 React 基本是指是代表著 View ,也就是說,他並沒有把一堆東西包的好好的,所以在早期的時候,要進行 Ajax ,可能還是要使用 jQuery ….

但,歷史已經經過了那麼久,我們還是必須要使用 XMLHttpRequest ,其實不然,現在技術大混戰的時期,怎麼可能不推出新的東西哩 ( 爆 ) ,所以這新的東西是甚麼… 就是未來會建立於各個瀏覽器內,新的呼叫方式 fetch .

fetch

那甚麼是 fetch 勒? 為什麼要有這東西,基本上這篇不會談到這個,有興趣的可參考小弟的參考資料,神人大大們寫的已經很詳細了。總之就是要取代掉 XMLHttpRequest 的東西。

那大家可能又會有疑問 “ Cool ,但現在所有瀏覽器都支援嗎? “ ,這個答案就如同 ES6 一樣,當然不支援阿 ( 狂暴 ) ,但雖然不支援,還是有很多神人寫出了 polyfill ,所以大家要知道的有兩件事情。

  1. fetch 是未來的一個呼叫標準。
  2. 使用這個標準寫出來的 JS Lib 稱為 fetch.js ,來相容不同的瀏覽器. ( 當然,除了 fetch.js 外,也有各神人寫出來不同的 Lib )。

如何使用

這才是這篇的重點,基本上也是小弟自己希望未來要 copy 比較方便,所以順便寫了這篇 XDDD
當然,要完整,還是建議看官網吧,小弟只是貼上自己常用的而已喔~~

Get

我們先用 Get 稍微看一下,基本上 fetch 他使用了 ES6 的 Promise ,所以後面都用 then 來接後續的動作。

所以第一個 then,response 那邊,我們可以先用 status 來判斷 http 給我們的 status code,如果是 200 內,我們才會在使用 response.json() 來將資料轉成 json 格式。

第二個 then 裡面的 data 才是真正的 json 物件.如果沒透過 then 基本上也只是個 Promise 物件而已喔~~

第三個是 catch ,可以捕捉 throw 的例外,而這個例外可以用 new Error 來產生。

最後,同樣的 error 裡面的資料,也必須使用response.json() 轉成物件,也同樣的,要最後一個 then 才能取得。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fetch('目標URL',{
method: 'GET',
}).then(function(response) {
if (response.status >= 200 && response.status < 300) {
return response.json()
} else {
var error = new Error(response.statusText)
error.response = response
throw error
}
})
.then(function(data) {
// data 才是實際的 JSON 資料
}).catch(function(error) {
return error.response.json();
}).then(function(errorData){
// errorData 裡面才是實際的 JSON 資料
});

總之,他回傳的都是 Promise 物件就是了~~

Post

如果是 Post,然後又是要用 JSON 溝通,我們可以加上 headers 這個標頭,而傳送的內容,則是放到 body 這個屬性裡面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fetch('網址',{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response.json();
} else {
var error = new Error(response.statusText)
error.response = response;
throw error;
}
})
.then(function(data) {
//完成
}).catch(function(error) {
console.log('request failed', error);
return error.response.json();
}).then(function(errorData){
//失敗
});

Token 驗證

這邊是自己在後端實作了 OAuth 的驗證,基本上要透過 POST 來傳送 x-www-form-urlencoded 格式的帳號密碼。
所以前面先進行了轉換並塞入到 formData 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let formData = Object.keys(userForm).map(function (keyName) {
return encodeURIComponent(keyName) + '=' + encodeURIComponent(userForm[keyName])
}).join('&');
fetch('網址',{
method: 'POST',
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
body: formData
}).then(function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response.json()
} else {
var error = new Error(response.statusText)
error.response = response
throw error
}
})
.then(function(data) {
//成功取得 Token
}).catch(function(error) {
console.log('request failed', error);
return error.response.json();
}).then(function(errorData){
//失敗
});

加上 Token 和 Get

這邊基本上就是加上了 Authorization 的 Header ,比較特別的是後面的 Bearer ${token},這是 ES6 串字串的方法。 ( token 是變數 )。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fetch('網址',{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
},
}).then(function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response.json()
} else {
var error = new Error(response.statusText)
error.response = response
throw error
}
})
.then(function(data) {
//成功取得 Token
}).catch(function(error) {
console.log('request failed', error);
return error.response.json();
}).then(function(errorData){
//失敗
});

搭配 Promise

如果要搭配 Promise,可以這樣寫,底下的範例是,當我們有動態的參數,且每次都必須確保所以請求都完成後,才要繼續作業,這個情況下,我們就可以使用 Promise.all 來進行處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
let promiseArray = [];
//用map 和 promiseArray 來收集 pormise , 有時因為網址會需要帶參數,
//而參數的內容在 datas 裡面,這邊 datas 裡面存放了 ID
datas.map( (id) => {
promiseArray.push(
fetch(`網址\id`,{
method: 'GET',
headers: {
//這邊需要驗證,所以要加上這行,沒有驗證的朋友就不用加了
'Authorization': `Bearer ${token}`
},
}));
});
//使用 Promise.all ,來讓所有請求完成,才繼續下一步
Promise.all(promiseArray) // 帶入 剛剛 map 回來的 promise Array
.then( (response) => {
// 回來的也是個陣列,而且陣列裡面的物件是 response
// 因為我們還要確保回傳的 Json 也是要全部處理完,才能繼續...
// 所以這邊再繼續用 Promise.all 來處理
// ( Promise.all 裡面帶的參數就是 promise Array
// 而我們用 response.map 回來的就是 promise 陣列)
Promise.all(response.map( (res) => {
if (res.status >= 200 && res.status < 300) {
return res.json()
}
}) //map end
) //promise all end
// 因為我是使用 Promise.all ,
// 所以這邊的 then 就是 全部傳完成後,傳回來的資料
.then( (data) => {
//這邊就看要怎樣處理資料了~~
//底下是用 forEach 來逐一列出資料
data.forEach( (d,i) => {
console.log(`data is : $(d) ; Index is ${i}`);
});
})
})

後記

基本上這篇就是稍微筆記筆記一下,相信未來應該會有人將這些東西包得更容易使用~~
同樣的,這個小弟也還在測試中,如有錯誤,有請多包涵喔!!~

參考資料