GET
GET request 不會有 Body,因為我只是想要取得一些資訊,我不需要告訴 server 任何東西
我只要發送 GET request 到特定的網址即可(server 會有不同的網址去處理不同的 GET request)
而 request Header 就是帶一些額外的資訊
POST
POST request 就會在 Body 放「我要傳送的資料」
而 request Header 就是帶一些額外的資訊
「server 會有不同的網址去處理不同的 request」,這句話的意思是:
Base URL 就是「這個 API 的網址」
一個 API 服務會提供很多不同的資源,不同的資源就會用不同的網址去做區分。只要在 Base URL 後面加上不同的 path,就是代表不同的資源
例如:
Base URL: https://lidemy-book-store.herokuapp.com
如果我要取得所有書籍的資料,就是要發一個 GET request 到 https://lidemy-book-store.herokuapp.com/books 這個網址去
透過 query string 更精準的描述「我想要拿到什麼資料」
要帶資訊到 server,有幾種方式:
- 第一種方式是用 POST
- 第二種方式是用「query string 」這個固定的格式:在網址後面加參數(?name=romeo 這些參數),例如 https://lidemy-http-challenge.herokuapp.com/lv1?name=romeo&age=28
參數中的 name, age 就是「key」,romeo, 28 就是「value」
因此我就可以把下面這個物件的資訊用網址傳送給 server,就可以這樣寫:
https://lidemy-http-challenge.herokuapp.com/lv1?name=romeo&age=28
const obj = {
name: 'romeo',
age: 28
}
為什麼會需要用網址來帶資訊呢?
當我用 GET 時,要帶資訊到 server 只有兩種方式:
- 用 Header 帶資訊
- 用 query string 在網址後面帶資訊,例如 https://lidemy-book-store.herokuapp.com/books?_limit=5 最後面帶上的
?_limit=5
就是 query string
當我用 GET 拿取資訊時,有時候我需要帶一些額外的資訊到 server,但又不能用 Header 帶,因為跟 Header 會帶的資訊是不同的種類
- 在網址會帶的資訊是:針對這個特定物件的資訊、針對這個特定的資源所要做的操作(例如:我只要拿幾筆資料)
- 在 Header 會帶的資訊是:更大範圍的資訊
例如:身份驗證-> Twitch API 的 server 會要求我要在 Header 裡面帶上一個'Client-ID': 'XXXXX'
來驗證「我這個人是否有註冊過」
因此就演變成:直接用網址來帶一些特定的資訊
例如:
在 week4 的 hw1,
當我用 GET 來拿取書籍資料時,如果我只是發一個 GET request 到網址 https://lidemy-book-store.herokuapp.com/books ,server 預設會回傳給我很多很多筆資料
但是我只想要 5 筆資料,我要怎麼把「只要 5 筆資料」這個資訊帶給 server 呢?
不可能用 POST(因為 POST 除了傳資料之外,其實有”新增“資料的意思)
因此,就要用「在網址後面帶參數」的方式把「只要 5 筆資料」這個資訊帶給 server (用 _limit
這個參數來限制回傳資料數量):
https://lidemy-book-store.herokuapp.com/books?_limit=5
這樣就可以只拿到 5 筆資料了!
用「request 這個套件」來發送一個 GET request
在網址後面帶上 query string
const request = require('request');
request(
'https://lidemy-book-store.herokuapp.com/books?_limit=5',
function (error, response, body) {
console.log(body);
}
);
執行 node index.js
之後,就可以拿到 5 筆書籍的資料了!
用 query string 帶的資訊,也可以用 Header 帶嗎?
不一定,要看 server 有沒有支援 Headers 和 query string 這兩種不同的資料來源
範例一:
https://lidemy-http-challenge.herokuapp.com/lv1?token=xxx
可以想成是:這串網址,會幫我呼叫一個遠端的 lv1
function,它會拿到我帶的 Headers 和 query string,如果這個 function 會把 Headers 和 query string 都做檢查,那就是 server 有同時支援 Headers 和 query string 這兩種資料來源(意思就是:我要用 Headers 或是 query string 都可以帶資訊)
但有時候,server 只會針對其中一個做檢查。
所以,API 文件會告訴我「資訊是要帶在 Headers 還是要帶在 query string 裡面」
function lv1(headers, queryString){
if(headers['token']...) ...
if(queryString) ...
}
範例二:
Twitch API 的文件 Getting a client ID 說「要傳送 client ID,Twitch API 有支援兩種方式」:
- 用 Request header
- 用 Query-string parameter
Content-Type
很重要的一個 request Header(根據 Content-Type
不同,Body 內容也會不同)
在 POST 的時候,要在 request Body 寫上「我要 POST 的資訊」
在 Body 所寫的內容,可以有很多種格式:
有可能是 name=romeo
或是 name:romeo
在 request Headers 中,有一項很重要的叫做 Content-Type
,當我用 POST 或是 PATCH 時,我要用 Content-Type
來告訴 server「我傳送的資料內容是什麼格式」
要用哪種 Content-Type
,也是要看 server 有支援哪種
最常見的格式有:
常見格式一:application/x-www-form-urlencoded
(表單格式)
用這種格式的話,Body 的內容就會像是這樣:
body: 'name=romeo&age=28'
例如:
在 request 套件的 Forms
request.post('http://service.com/upload', {
form: { name: 'romeo' }
})
這段的意思就是:我要用 POST method 發送一個 request 到 http://service.com/upload 這個網址,同時帶上一個「表單格式」的資料是 { name: 'romeo' }
當我在 Body 用 name: 'romeo'
這樣的格式寫完資料後,當這個 POST request 真的被發送出去時,request 套件會幫我把 Content-Type
這個 request Header 自動填上 application/x-www-form-urlencoded
這個格式(幫我跟 server 說:我現在的 Body 格式是 application/x-www-form-urlencoded
)。
然後,會把 { name: 'romeo' }
這個物件,按照「表單格式」轉成 'name=romeo'
這樣的字串,再放到 Body 裡面。
當 server 收到 request Header 寫的 Content-Type: application/x-www-form-urlencoded
時,才知道要怎麼去解析 'name=romeo'
這樣的字串
因此,儘管帶的資料是正確的 'name=romeo'
,但如果亂寫一個 Content-Type: kjjmmmm
,server 也不會知道要怎麼去解析我的資料
application/x-www-form-urlencoded
之所以會叫做「表單格式」,是因為在 html 的表單要送出元素時,也是用這種格式來送出
常見格式二:multipart/form-data
multipart/form-data
這個格式通常是用來上傳檔案(例如:圖片)用的
常見格式三:application/json
如果我的 request Header 是寫 Content-Type: application/json
,就是在跟 server 說:我在 request Body 傳送的內容會是一個 json 格式的字串
因此,在寫 POST 時,就要自己用 JSON.stringify()
來把「JavaScript 的物件」轉成一個「json 格式的字串」:
request.post({
url: 'https://api.github.com/repos/request/request',
headers: {
'content-type': 'application/json'
}
},
{
body: JSON.stringify({ name: 'romeo', age: 28 })
},
callback);
因此,body: JSON.stringify({ name: 'romeo', age: 28 })
這句就會變成是 body: "{"name":"romeo","age":28}"
(json 格式的字串)
當我在 request 套件用 Forms 傳資料時,用 request 包裝過後的形式來寫,比較簡短方便:
request.post('http://service.com/upload', {
form: {
name: 'romeo',
age: 28
}
})
就等同於是下面這段:用物件來寫,彈性比較大,可以自己加上很多東西
但是因為用物件寫這段太麻煩了,所以 request 套件才會把這段包裝成上面那樣很簡短的形式,方便開發者去寫
request({
method: 'POST',
url: 'http://service.com/upload',
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
body: 'name=romeo&age=28'
})
application/x-www-form-urlencoded
會碰到的問題
假設,在我要傳的資料中,有一項是 str: 'a&b=20'
request.post('http://service.com/upload', {
form: {
name: 'romeo',
age: 28,
str: 'a&b=20'
}
})
那在轉成表單格式後,就會是這樣:
request({
method: 'POST',
url: 'http://service.com/upload',
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
body: 'name=romeo&age=28&str=a&b=20'
})
這句 str=a&b=20
會造成 server 的混淆(中間的 &
讓它變成兩個參數)
在 application/x-www-form-urlencoded
格式,其中的 encode 是「編碼」的意思-> 像是把英文字母變成摩斯密碼這樣,一個英文字母就對應到一個摩斯密碼(只是不同資料形式的轉換而已)
所以,當我傳的資料會造成混淆時,就可以先經過 encode 變成一種特殊的格式後,再傳給電腦,電腦解碼後就會知道我原本要傳的資料是什麼
用 encodeURIComponent()
編碼
在 JavaScript 有一個 function 可以用,叫做 encodeURIComponent()
,小括號裡面放入我要 encode 的字串
在 Console 輸入:
encodeURIComponent('a&b=20')
會得到:
"a%26b%3D20"
可以推敲出:
&
被編碼後會是%26
=
被編碼後會是%3D
當 server 看到 %26
就會知道它是 &
因此,在 Body 裡面就可以改成用編碼過後的 str=a%26b%3D20
:
這樣,就不會因為 value 有 &
和 =
而造成 server 的混淆了
request({
method: 'POST',
url: 'http://service.com/upload',
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
body: 'name=romeo&age=28&str=a%26b%3D20'
})
但是有些時候,要傳的資料內容是動態的,我也無法預測會不會有這些會讓 server 混淆的字元出現。所以,就直接會先把每個 value 都進行編碼,這樣最保險