PJCHENder 未整理筆記

[WebAPIs] Fetch API

2018-03-13

[WebAPIs] Fetch API

@(JavaScript)[JavaScript, WebAPIs]

keywords: Fetch API, AJAX

Fetch API Example @ Gist

Simple Get Request:

1
2
3
4
5
// Simple Get Request
fetch('<resourceURL>', init)
.then(response => response.json()) // response.ok, response.status, response.statusText
.then(json => console.log(json))
.catch(error => console.log(error))

:exclamation: 要留意的是,即使回應是 404 該 Promise 依然會被 resolve,只有在該請求沒辦法被完成時(例如,無網路連線),才會被 reject 進而進到 catch,因此,**需要自己判斷 response.ok **,如果不 ok 則透過 throw 讓它進入 catch

With Error Handling:

1
2
3
4
5
6
7
8
9
fetch('<resourceURL>', init)
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error('Network response was not ok.');
})
.then(data => console.log(json))
.catch(error => console.log(error))

Example:

See the Pen Fetch API sandbox by PJCHEN (@PJCHENder) on CodePen.

Fetch API

最簡單使用 fetch() 的方式就是帶入一個參數,讓入你想要發送請求的資源(resource),接著會回傳包含 Response 物件的 Promise。但這個 Response 只是 HTTP Response,我們可以透過 json() 方法來取出 response 中的 JSON 部分:

1
2
3
4
5
// Example GET method request
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(json => console.log(json))
.catch(error => console.log(error))

Init Options

fetch() 的第二個參數中可以放入其他相關的設定:

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
// Example POST method implementation
let data = {
title: 'foo',
body: 'bar',
userId: 1
}

postData('https://jsonplaceholder.typicode.com/posts', data)
.then(data => console.log(data)) // JSON from `response.json()` call
.catch(error => console.error(error))

const init = {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
body: JSON.stringify(data), // must match 'Content-Type' header
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json; charset=UTF-8',
'Accept': 'application/json'
},
cache: 'no-cache',
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // *manual, follow, error
referrer: 'no-referrer' // *client, no-referrer
}

function postData(url, data) {
// Default options are marked with *
return fetch(url, init)
.then(response => response.json()) // parses response to JSON
}

/* will return
{
id: 101,
title: 'foo',
body: 'bar',
userId: 1
}
*/

HEAD Method:在 HTTP Method 中,HEAD 方法和 GET 方法的效果一樣,差別只在於透過 HEAD 方法拿到的 response.body 會是空的,通常用在只需要取得檔案的 metadata 時使用。

Response

在透過 fetch API 取得的 response 中包含一些可用的屬性:

  • response.ok:如果回應的狀態介於 200-299 之間,則會是 true
  • response.status:回應狀態代碼
  • response.statusText
  • response.redirected:是否轉址,回傳 truefalse
  • response.url:發送請求的 url

另外提供了一些可用的方法:

  • response.json()
  • response.blob()
  • response.text()
  • response.arrayBuffer()
  • response.formData()

Response Interface @ MDN - Web APIs

範例程式碼

Fetch API Sandbox @ PJCHENder CodePen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Example
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => {
if (!response.ok) {
throw Error(response.statusText);
}

// Read the response as json.
return response.json();
})
.then(responseAsJson => {
// Do stuff with the JSON
console.log(responseAsJson);
})
.catch(error => {
console.log('Looks like there was a problem: \n', error);
});

如果在 Promise 中需要串連的方法較多,可以考慮把方法抽出來:

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
// Example
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(validateResponse)
.then(parseResponseToJSON)
.then(doSomething)
.catch(errorHandler);

function validateResponse(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response
}

function parseResponseToJSON(response) {
return response.json();
}

function doSomething(data) {
console.log(data);
}

function errorHandler(error) {
console.log(error)
}

其他

檢驗瀏覽器是否支援 fetch

1
2
3
4
5
if (!('fetch' in window)) {
console.log('Fetch API not found, try including the polyfill');
return;
}
// We can safely use fetch from now on

使用 Fetch 取得圖片(blob)

在透過 fetch 取得圖片時,會得到的是 blob 物件,因此需要使用 response.blob() 方法:

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
fetchImage('https://fakeimg.pl/250x100/')

function fetchImage(imageURL) {
fetch(imageURL)
.then(validateResponse)
.then(parseResponseToBlob)
.then(appendImage)
.catch(errorHandler)
}

function validateResponse(response) {
if (response.ok) {
return response;
}
throw Error(response.statusText);
}

function parseResponseToBlob(response) {
return response.blob();
}

function appendImage(blob) {
const img = new Image(250, 100);
img.src = URL.createObjectURL(blob)
document.querySelector('body').appendChild(img)
}

function errorHandler(error) {
console.log(error);
}

使用 POST 傳送 form data

透過 new FormData() 的方法,可以將 HTML 中的表單元素傳送到後端:

1
2
3
4
5
// Assuming an HTML <form> with id of 'myForm'
fetch('some-url/comment', {
method: 'POST',
body: new FormData(document.getElementById('myForm'))
})

自訂標頭(Headers)

透過 new Header() 的方法,可以為 Fetch Request 建立自訂的標頭(Header):

1
2
3
4
5
6
7
8
var myHeaders = new Headers({
'Content-Type': 'text/plain',
'X-Custom-Header': 'hello world'
});

fetch('/some-url', {
headers: myHeaders
});

注意:Content-LengthOrigin 等標頭基於安全理由是無法被修改的。設置客制化標頭時,瀏覽器會自動發送預檢請求(preflight request)。

參考

掃描二維條碼,分享此文章