cover picture sponsor: gleammming.art
在現實生活中我們隨處可見到類似於請求與響應的概念,例如當我們搭大眾交通工具時,若想查詢卡片的餘額時,我們只要透過加值機或是超商的感應機就能夠查詢到裡面還有多少餘額。
而當我們將卡片放在機器上的那一刻,機器就會發送一個請求向伺服器查詢,而伺服器在接收到請求之後,再回應資料回來,最後顯示在螢幕上供我們參考。
像這樣的請求與響應,在前端網頁開發的領域中,更是相當常見的一環。一名注重使用者體驗的開發設計者 Jesse James Garrett,也特別將這個一連串綜合的技術命名為—— AJAX。
AJAX,為 Asynchronous JavaScript and XML 的縮寫,意思即為非同步的 JavaScript 與 XML。之所以這項技術會被關注的原因在於,在早期的網頁資源請求往往是同步(Synchronous)的執行,並依賴於伺服器回應一個新的頁面,因此在互動性與效能上的體驗都不太友善,且當時網路的傳輸並沒有現在來得這麼快,所以對於網頁開發者來說他們勢必得找到一個更友善的方式來解決這些問題,而那個方式就是透過 AJAX 來處理資源請求上的相關議題。
在 AJAX 系列技術中,第一個要面臨的是頁面請求問題,由於要求回應整個頁面既耗時又會產生不必要的浪費,因此我們改由向伺服器請求的回傳特定部分的 XML 檔案,而後續由於 JSON 格式的資料比起 XML 來說更加地通用、方便,因此現在有關於資料回應的大部分都改由回傳一個 JSON 格式資料的做法來處理:
{
"status": 200,
"data": [{
"id": "cat",
"name": "Orange",
"age": 3
}]
}
JSON (JavaScript Object Notation)意思即為 JavaScript 物件表示法,是一種基於 JavaScript 物件中所設計出的一套標準格式,透過這種資料格式比起 XML 格式來說都要來得更為輕巧且在 JavaScript 程式當中可直接使用,而通用性方面也可以用於其他語言(如 Java)或是 NoSQL 資料庫等等。
接著,由於先前的請求都是同步的執行,因此當使用者點選某個需要發送表單或是請求資料的按鈕後,就勢必得等到他執行完畢並回傳結果時才能繼續操作,如此一來使用者體驗將會大幅降低,因此另一個重要的概念是在於該技術必須要能夠 非同步(Asynchronous) 的執行。
然而,當 JavaScript 程式碼在瀏覽器執行的過程中,只有一條負責運行的主線程(Main thread),因此為了在瀏覽器上要實現非同步的效果,瀏覽器必須提供一套非同步的機制才能將我們所要執行的程式碼,暫時的移出 我們的主線程當中,最後經由一連串的機制再來執行。
非同步機制部分我們將會在第四章節中有更詳細的介紹。
現在我們瞭解了需要非同步請求資源的原因之後,接著就要來看看要怎麼透過 JavaScript 向瀏覽器發送一個非同步的方法來取得資料。
而在現代網頁開發中已經有許多方案、函式庫可供發起非同步的請求,其概念上大同小異,主要差異在 瀏覽器的支援性、可控制的細節多寡 以及 背後實作上的技術不同 而已,因此下面我將介紹 fetch
來幫助你快速理解它們大致上的概念:
使用 fetch
發送請求的方式非常簡單,你只需要透過 fetch
中的第一個引數中放入網址即可向該網址所屬的伺服器發送請求:
fetch('https://raw.githubusercontent.com/shawnlin0201/ithelp-2020/main/3-4-1-fetch-request.json')
由於 fetch
背後是基於 Promise
物件而實作出來的,因此我們可以透過 then
來取的回應中的內容,並透過 json()
,將回應的內容轉成 JSON
格式。
fetch('https://raw.githubusercontent.com/shawnlin0201/ithelp-2020/main/3-4-1-fetch-request.json')
.then(function(response) {
return response.json()
})
接著再一次透過 then
取得 response.json()
所解析完的 JSON 格式資料,而回應的結果就如同以下範例:
fetch('https://raw.githubusercontent.com/shawnlin0201/ithelp-2020/main/3-4-1-fetch-request.json')
.then(function(response) {
return response.json()
})
.then(function(response){
console.log(response)
// [
// {
// "id": "cat001",
// "name": "Orange",
// "age": 3
// },
// {
// "id": "cat002",
// "name": "Black",
// "age": 5
// }
// ]
})
後續我們就可以依靠這個資料來出對應的功能,比如將其顯示為一個列表:
HTML 部分:
<ul class="cat-list">
</ul>
JavaScript 部分:
fetch('https://raw.githubusercontent.com/shawnlin0201/ithelp-2020/main/3-4-1-fetch-request.json')
.then(function(response) {
return response.json()
})
.then(function(response){
let catInfo = response
let list = ''
for(var i = 0; i < catInfo.length; i++) {
list += '<li>' + catInfo[i].name + 'is ' + catInfo[i].age + ' years old.</li>'
}
console.log(list) // <ul><li>Orangeis 3 years old.</li><li>Blackis 5 years old.</li></ul>
document.querySelector('.cat-list').innerHTML = list
})
當我們請求資源的時候,中間可能會受到一些通訊上的問題,而無法傳遞資料,這時候我們就可以透過 HTTP 狀態碼(HTTP Status Code) 來瞭解相關的資訊:
fetch('https://raw.githubusercontent.com/shawnlin0201/ithelp-2020/main/3-4-1-fetch-request.json')
.then(function(response) {
console.log(response.status) // 200
return response.json()
})
.then(function(response){
console.log()
})
在 fetch
中我們可以在第一次伺服器回應資料的過程中透過底下的 status
來取得 HTTP 狀態碼,而這些狀態碼主要以三碼來呈現,並以開頭號碼來作為大致上的類別:
其中最常見的大概就屬 200(成功)以及 404(找不到資源)。
而這個狀態碼除了在開發過程中可以幫助開發者瞭解當下請求的狀態之外,後續也可以根據狀態碼來作為程式碼邏輯流程的判斷方法之一,但多數的請求方法中可能會有不同的除錯方式,例如 fetch
是使用 catch
來作為主要除錯。
fetch('...')
.then(res => res.json)
.catch(e => e) // 在這裡處理萬一錯誤時的後續流程
.then(res => console.log(res))
除了狀態碼之外,在請求資源的同時,其實我們需要同時告訴伺服器我們要請求的事件是屬什麼類別,而這些行為類別我們稱呼為 HTTP 方法(HTTP request methods),最常見的方法有下列幾種:
在使用 GET 方法時,就像是在告訴伺服器說,我們單純只是要取得資料,並沒有要求伺服器去做什麼變更,而在 fetch
中預設的請求方法就是 GET
,因此我並不需要額外設定:
fetch('https://raw.githubusercontent.com/shawnlin0201/ithelp-2020/main/3-4-1-fetch-request.json')
.then(res => res.json())
.then(res => {
console.log(res)
})
然而使用 POST 方法時,則除了要求伺服器要有所回應外,通常還代表伺服器端也要相應做一些改變,一般來說可能像是新增資料、刪除資料等等行為。
在 fetch
函式中則可以透過第二個引數來設定相關的資料:
fetch('https://raw.githubusercontent.com/shawnlin0201/ithelp-2020/main/3-4-1-fetch-request.json',{
body: JSON.stringify([{
"id": "cat001",
"name": "Orange",
"age": 3
},{
"id": "cat002",
"name": "Black",
"age": 5
},{
"id": "cat003",
"name": "none",
"age": 0
}]),
headers: {
'content-type': 'application/json'
},
method: 'POST',
})
.then(res => res.json())
.then(res => {
console.log(res)
})
其中的 body
則是透過 JSON.stringify()
來將我們預期要變更的資料轉成符合 JSON 格式的字串,以方便資料的傳輸與後續的解析。
header
的部分則是在告訴伺服器說我們傳過去的資料將會以 application/json
的格式作為傳遞。
接著 method
自然是告訴伺服器我們預期想做的事情是 POST
(變更的含義)。
透過這些請求(request)的設定值,便可以使伺服器端較為清楚的瞭解我們的請求以便回覆我們對的內容並進行後續的處理。
以上就是資源請求的基本介紹,明天我們將繼續深入探討幾個在做資源請求的過程中常常會遇到的問題!