XMLHttpRequest
- JavaScript 原生的方法傳送請求
- 不支援 Promises
- 語法攏長,較多 callbacks,不好記
GET request
// GET request
const myReq = new XMLHttpRequest();
myReq.onload = function() {
const data = JSON.parse(this.responseText);
};
myReq.onerror = function(err) {
console.log('Error!', err);
};
myReq.open('get', 'https://www.icanhazdadjoke.com/', true');
myReq.setRequestHeader('Accept', 'application/json');
myReq.send();
POST request
// POST request
let xhr = new XMLHttpRequest();
xhr.open('POST', 'https://hexschool-tutorial.herokuapp.com/api/signup', true);
// 格式:application/x-www-form-urlencoded
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send('email=abcde@gmail.com');
// 格式:JSON
// let account = {
// email:'abcdef@gmail.com',
// password: '1234'
// };
// xhr.setRequestHeader("Content-type", "application/json");
// xhr.send(JSON.stringify(account));
範例:GET Request StarWars API (www.swapi.dev)
const req = new XMLHttpRequest();
req.addEventListener('load', function() {
console.log('請求成功!');
const data = JSON.parse(this.responseText);
for (let planet of data.results) {
console.log(planet.name); // 列出星球名稱
}
});
req.addEventListener('error', () => {
console.log('錯誤');
});
req.open('GET', 'https://swapi.dev/api/planets/')
req.send();
console.log('request send')
Planet Request 結果:第一筆資料
GET /api/planets/
HTTP 200 OK
Content-Type: application/json
Vary: Accept
Allow: GET, HEAD, OPTIONS
{
"count": 60,
"next": "http://swapi.dev/api/planets/?page=2",
"previous": null,
"results": [
{
"name": "Tatooine",
"rotation_period": "23",
"orbital_period": "304",
"diameter": "10465",
"climate": "arid",
"gravity": "1 standard",
"terrain": "desert",
"surface_water": "1",
"population": "200000",
"residents": [
"http://swapi.dev/api/people/1/",
"http://swapi.dev/api/people/2/",
"http://swapi.dev/api/people/4/",
"http://swapi.dev/api/people/6/",
"http://swapi.dev/api/people/7/",
"http://swapi.dev/api/people/8/",
"http://swapi.dev/api/people/9/",
"http://swapi.dev/api/people/11/",
"http://swapi.dev/api/people/43/",
"http://swapi.dev/api/people/62/"
],
"films": [
"http://swapi.dev/api/films/1/",
"http://swapi.dev/api/films/3/",
"http://swapi.dev/api/films/4/",
"http://swapi.dev/api/films/5/",
"http://swapi.dev/api/films/6/"
],
"created": "2014-12-09T13:50:49.641000Z",
"edited": "2014-12-20T20:58:18.411000Z",
"url": "http://swapi.dev/api/planets/1/"
},
...略
Chaining XMLHttpRequest
每一個星球下還有很多資料,例如 films,但是要取的 films 還需要發送 request 才可以取得資料。
Films request 結果
// Film Instance
GET /api/films/5/
HTTP 200 OK
Content-Type: application/json
Vary: Accept
Allow: GET, HEAD, OPTIONS
{
"title": "Attack of the Clones",
"episode_id": 2,
"opening_crawl": "There is unrest in the Galactic\r\nSenate. Several thousand solar\r\nsystems have declared their\r\nintentions to leave the Republic.\r\n\r\nThis separatist movement,\r\nunder the leadership of the\r\nmysterious Count Dooku, has\r\nmade it difficult for the limited\r\nnumber of Jedi Knights to maintain \r\npeace and order in the galaxy.\r\n\r\nSenator Amidala, the former\r\nQueen of Naboo, is returning\r\nto the Galactic Senate to vote\r\non the critical issue of creating\r\nan ARMY OF THE REPUBLIC\r\nto assist the overwhelmed\r\nJedi....",
"director": "George Lucas",
"producer": "Rick McCallum",
"release_date": "2002-05-16",
"characters": [
"http://swapi.dev/api/people/2/",
"http://swapi.dev/api/people/3/",
"http://swapi.dev/api/people/6/",
"http://swapi.dev/api/people/7/",
"http://swapi.dev/api/people/10/",
.... 略
Film 底下還有很多資料可以 request,例如人物。如果用 XMLHttpRequest 就可以一直 nested 下去沒完沒了。
Fetch
- ES6 原生方法
- 支援 Promises
- 不支援 IE
POST Request
// 來發個 POST Request:
postData('http://example.com/answer', {answer: 42})
.then(data => console.log(data)) // JSON from `response.json()` call
.catch(error => console.error(error))
function postData(url, data) {
// Default options are marked with *
return fetch(url, {
body: JSON.stringify(data), // must match 'Content-Type' header
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, *omit
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
},
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
})
.then(response => response.json()) // 輸出成 json
}
HexSchool 註冊帳號範例:
const api = 'https://hexschool-tutorial.herokuapp.com/api/signup';
let account = {
email:'abcdef@gmail.com',
password: '1234'
};
postData(api, account)
.then(data => console.log(data)) // JSON from `response.json()` call
.catch(error => console.error(error))
function postData(url, data) {
return fetch(url, {
body: JSON.stringify(data),
headers: {
'content-type': 'application/json'
},
method: 'POST',
})
.then(response => response.json()) // 輸出成 json
}
// {success: false, result: {…}, message: "此帳號已被使用"}
GET Request
const myReq = fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
});
範例:改寫 XMLHttpRequest 例子
const fetchedData = fetch('https://swapi.dev/api/planets/')
.then( response => {
// status: 404/500 promise 一樣會是 resolved (ok 值會從 true 變成 false)
console.log(response);
if (response.status !== 202) {
console.log('發生錯誤:', response.status);
return;
}
response.json().then ( data => {
for (let planet of data.results) {
console.log(planet.name);
}
})
})
.catch( err => {
// 只有在網路發生錯誤或是請求中斷才會是 promise rejected
console.log('Error: ', err);
})
fetch() 回傳的 promise 物件, resolve 和 reject 的使用方式有差異, 當遇到 HTTP Status 404
, 500
時會使用 resolve 但會將 status 的值從 ok 變為 false, reject 只有在網路發生錯誤或是任何會中斷網路請求時才會使用。 — MDN web docs
除了上方的方式,也可以利用下方的 catch error,即是把錯誤丟給他
const fetchedData = fetch('https://swapi.dev/api/planetsss/') // 網址錯誤
.then( response => {
// status: 404/500 promise 一樣會是 resolved (ok 值會從 true 變成 false)
console.log(response);
if (response.status !== 202) { // 這邊寫成 if (!response.ok) ** not ok ** 也可以
throw new Error(`Status: ${response.status}`);
return;
}
response.json().then ( data => {
for (let planet of data.results) {
console.log(planet.name);
}
})
})
.catch( err => {
// 只有在網路發生錯誤或是請求中斷才會是 promise rejected
console.log('Catch 錯誤: ', err);
})
錯誤跑到下方顯示了。
const fetchedData = fetch('https://swapi.dev/api/planets/')
.then( response => {
// status: 404/500 promise 一樣會是 resolved (ok 值會從 true 變成 false)
console.log(response);
if (!response.ok) {
throw new Error(`Status: ${response.status}`);
return;
}
return response.json(); // return promise
})
////////////////////////////////
// 從上方 response.json() 往下移,less nested
.then ( data => {
console.log(data);
for (let planet of data.results) {
console.log(planet.name);
}
})
////////////////////////////////
.catch( err => {
// 只有在網路發生錯誤或是請求中斷才會是 promise rejected
console.log('Catch 錯誤: ', err);
})
Chaining Fetch Requests
const fetchedData = fetch('https://swapi.dev/api/planets/')
.then( response => {
// status: 404/500 promise 一樣會是 resolved (ok 值會從 true 變成 false)
console.log('星球', response);
if (!response.ok) {
throw new Error(`Status: ${response.status}`);
return;
}
return response.json(); // return promise
})
.then ( data => {
//////////////////////////////
// request FILM
const filmAPI = data.results[0].films[0];
fetch(filmAPI)
.then( response => {
console.log('影片', response);
if (!response.ok) {
throw new Error(`Status: ${response.status}`);
return;
}
return response.json(); // return promise
})
.then ( filmData => {
console.log('filmData', filmData);
})
.catch( err => {
console.log('Catch 錯誤: ', err);
})
})
////////////////////////////////
.catch( err => {
// 只有在網路發生錯誤或是請求中斷才會是 promise rejected
console.log('Catch 錯誤: ', err);
})
const fetchedData = fetch('https://swapi.dev/api/planets/')
.then( response => {
// status: 404/500 promise 一樣會是 resolved (ok 值會從 true 變成 false)
console.log(response);
if (!response.ok) {
throw new Error(`Status: ${response.status}`);
return;
}
return response.json(); // return promise
})
////////////////////////////////
// 請求 film data
.then ( data => {
const filmAPI = data.results[0].films[0];
return fetch(filmAPI); // return promise
})
//** 這邊跟上方的 response 是做一樣的事 **//
.then( response => {
console.log(response);
if (!response.ok) {
throw new Error(`Status: ${response.status}`);
return;
}
return response.json(); // return promise
})
.then( filmData => {
console.log(filmData);
})
//** **//
////////////////////////////////
.catch( err => {
// 只有在網路發生錯誤或是請求中斷才會是 promise rejected
console.log('Catch 錯誤: ', err);
})
這樣子看起來比較平整話,而且有部分重複的語法。
AXIOS
- 支援 promises
- 在瀏覽器中創建 XMLHttpRequests
- 支援IE
Post Request
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
HexSchool 註冊帳號範例:
const api = 'https://hexschool-tutorial.herokuapp.com/api/signup';
axios.post(api, {
email:'abcdefddd@gmail.com',
password: '1234'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
Reference:
The Modern JavaScript Bootcamp
Using Fetch — MDN web docs
AXIOS