JavaScript Fetch API 使用教學
自從 Fetch API 問世以來,我們就能使用漂亮的語法發送 HTTP Request 或取得網路資料,這篇文章將會分享我自己常用的 Fetch 方法 ( GET、POST、搭配 await 或 promise.all...等 ),隨著瀏覽器的普遍支援,也就不太需要使用 XMLHttpRequest 或 jQuery AJAX,程式碼也就更加簡潔乾淨囉~
Fetch 基本用法
fetch()
是一個全域的方法,包含了需要 fetch 的網址和對應的屬性設定 ( 例如 method、headers、mode、body...等,最基本的寫法屬性不一定要填 ),執行之後會送出 Request,如果得到回應就會回傳帶有 Response 的 Promise 物件,使用 then 將回傳值傳遞下去。
fetch('網址')
.then(function(response) {
// 處理 response
}).catch(function(err) {
// 錯誤處理
});
舉例來說,前往 中央氣象局開放資料平台可以取得許多氣象資料,下面的範例使用 局屬氣象站-現在天氣觀測報告,複製 JSON 格式的連結 ( 需要註冊登入才能看得到連結 ),因為檔案為 json 格式,所以在 fetch 取得檔案之後,透過json()
的方法處理檔案,接著傳遞到下一層,就能顯示出「高雄市的即時氣溫」。
fetch('局屬氣象站-現在天氣觀測報告網址')
.then(res => {
return res.json();
}).then(result => {
let city = result.cwbopendata.location[14].parameter[0].parameterValue;
let temp = result.cwbopendata.location[14].weatherElement[3].elementValue.value;
console.log(`${city}的氣溫為 ${temp} 度 C`); // 得到 高雄市的氣溫為 29.30 度 C
});
Fetch 的 Request 屬性
以下列出 Fetch 常用的的 Request 屬性。( 更多屬性請參考 fetch Request )
屬性 | 設定值 |
---|---|
url | 第一個參數,一定要填的項目,代表需要 fetch 對象的網址 |
method | GET、POST、PUT、DELETE、HEAD ( 預設 GET ) |
headers | 要求相關的 Headers 物件 ( 預設 {} ) |
mode | cors、no-cors、same-origin、navigate ( 預設 cors ) |
referrer | no-referrer、client 或某個網址 ( 預設 client ) |
credentials | omit、same-origin、include ( 預設 omit ) |
redirect | follow、error、manual ( 預設 manual ) |
cache | default、no-store、reload、no-cache、force-cache ( 預設 default ) |
body | 要加到要求中的內容 ( 如果 method 為 GET 或 HEAD 則不設定 ) |
Fetch 的 Response 屬性
以下列出 Fetch 常用的 Response 屬性。( 更多屬性和方法請參考 fetch Response )
屬性 | 設定值 |
---|---|
headers | 包含與 response 相關的 Headers 物件 |
ok | 成功回傳 true,不成功回傳 false |
status | 狀態代碼,成功為 200 |
statusText | 狀態文字,成功為 ok |
type | response 的類型,例如 basic、cors...等 |
url | response 的 url |
Fetch 的 Response 方法
以下列出 Fetch 常用的 Response 方法。( 更多屬性和方法請參考 fetch Response )
方法 | 設定值 |
---|---|
json() | 返回 Promise,resolves 是 JSON 物件 |
text() | 返回 Promise,resolves 是 text string |
blob() | 返回 Promise,resolves 是 blob ( 非結構化物件資料,例如文字或二進位資料 ) |
arrayBuffer() | 返回 Promise,resolves 是 ArrayBuffer ( 有多少 bytes ) |
formData() | 返回 Promise,resolves 是 formData ( 表單資料對應的的 Key 或 Value ) |
clone() | 建立 Response 的複製物件 |
error() | 返回 Response 的錯誤物件 |
Fetch 的 Get 用法
Get 是 Fetch 最簡單的方法,使用 Get 必須要將 fetch 第二個參數裡的 method 設定為 get,如果遇到跨域問題,就搭配其他屬性例如 mode、credentials 來進行細部設定 ( 但針對非跨域的資料就沒用了 ),下方的範例我做了一個簡單的後端程式,透過 fetch 傳遞姓名和年紀的參數,就會看到後端回應一串文字。
const name = 'oxxo';
const age = 18;
// 有興趣的可以使用下方的網址測試
const uri = `https://script.google.com/macros/s/AKfycbw5PnzwybI_VoZaHz65TpA5DYuLkxIF-HUGjJ6jRTOje0E6bVo/exec?name=${name}&age=${age}`;
fetch(uri, {method:'GET'})
.then(res => {
return res.text(); // 使用 text() 可以得到純文字 String
}).then(result => {
console.log(result); // 得到「你的名字是:oxxo,年紀:18 歲。」
});
Fetch 的 Post 用法
使用 POST 方法可以搭配 body 屬性設定傳遞參數,下方的測試網址,可以接收 name 和 age 所組成的物件,當網址接收到要求後,就會回應一個 json 物件,比較需要注意的是,如果是傳遞「中文」可能會出現亂碼,這時可以使用encodeURI
來做轉碼,且要透過JSON.stringify
來轉換成 string 方式傳遞。
const uri = 'https://script.google.com/macros/s/AKfycbxXH6aPsldTBeS41WRMnJEA5Xstc7cYMj6YimDO2Al7H6DkJZiz/exec';
fetch(uri, {
method:'POST',
body:encodeURI(JSON.stringify({
name:'oxxo',
age:18
})),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
}
})
.then(res => {
return res.json(); // 使用 json() 可以得到 json 物件
}).then(result => {
console.log(result); // 得到 {name: "oxxo", age: 18, text: "你的名字是 oxxo,年紀 18 歲~"}
});
Fetch 搭配 async、await、promise.all
過去在 XMLHttpRequest 或 jQuery AJAX 的全盛時期,如果要確保每個 GET 或 POST 的要求,都要按照指定的順序進行,往往會用上一連串的 callback 輔助,但是當 callback 越來越多,程式碼也就越來越難管理,然而 fetch 回傳的是一個 Promise,我們也就能直接利用 await 或 promise.all 的作法,輕鬆掌握同步與非同步之間的轉換。
下方的例子是一個非同步的程式碼,因為沒有進行任何的同步處理,所以執行之後,會「先出現 hello 的文字,接著才是透過 fetch 得到的結果。
const postURL = (name,age) => {
const uri = 'https://script.google.com/macros/s/AKfycbxXH6aPsldTBeS41WRMnJEA5Xstc7cYMj6YimDO2Al7H6DkJZiz/exec';
return fetch(uri, {
method:'POST',
body:encodeURI(JSON.stringify({
name:name,
age:age
})),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
}
})
.then(res => {
return res.json();
}).then(result =>{
console.log(result);
});
};
postURL('oxxo',18);
console.log('hello!!!');
postURL('tom',18);
因為 fetch 的特性,可以改成 async 和 await 的寫法,執行後也就能按照我們要的順序進行。
~async function(){ // 開頭設定為 async
const postURL = (name,age) => {
const uri = 'https://script.google.com/macros/s/AKfycbxXH6aPsldTBeS41WRMnJEA5Xstc7cYMj6YimDO2Al7H6DkJZiz/exec';
return fetch(uri, {
method:'POST',
body:encodeURI(JSON.stringify({
name:name,
age:age
})),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
}
})
.then(res => {
return res.json();
}).then(result =>{
console.log(result);
});
};
await postURL('oxxo',18); // 使用 await
console.log('hello!!!');
await postURL('tom',18); // 使用 await
}();
最後那段 await 的程式碼,也可以改成 promise.all 的作法,就會先 fetch,然後再出現 hello 的文字,不過也因為 promise.all 無法保證其載入順序,就可能會發生 tom 在 oxxo 之前出現的狀況呦。
await Promise.all([postURL('oxxo',18), postURL('tom',18)]);
console.log('hello!!!');
小結
Fetch API 的神奇,簡化了許多原本較為複雜的用法,也讓程式碼寫起來更加乾淨易讀好維護,我的 Blog 現在也是使用 Fetch 呦~ 推薦給大家~~
更多參考資源:
備註
上面的範例我都是使用 Google Apps Script 做簡易後端,但在接收 Fetch POST 方法松出的資料時,接收的內容卻跟以往不太相同,因此在 Apps Script 的取值要改成下面的寫法,出來的結果才會是正常的~
詳細介紹請參考我之前的文章:簡易後端實作 ( Google Apps Script )
function doPost(e) {
var param = JSON.parse(Object.keys(e.parameter)[0]); // 這段是重點!!!!
var name = param.name;
var age = param.age;
var replyMsg = {
name: name,
age: age,
text: '你的名字是 '+name+',年紀 '+age+' 歲~'
};
var JSONString = JSON.stringify(replyMsg);
return ContentService.createTextOutput(JSONString).setMimeType(ContentService.MimeType.JSON);
}