Request 額外補充


Posted by saffran on 2021-02-06

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,有幾種方式:

  1. 第一種方式是用 POST
  2. 第二種方式是用「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 只有兩種方式:

  1. 用 Header 帶資訊
  2. 用 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 有支援兩種方式」:

  1. 用 Request header
  2. 用 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 都進行編碼,這樣最保險


#Network







Related Posts

Geolocation based Speedometer and Compass

Geolocation based Speedometer and Compass

滲透測試重新打底(3.6)--論Web入侵之SSRF攻擊

滲透測試重新打底(3.6)--論Web入侵之SSRF攻擊

如何使用 Python 實現 LRU Cache 快取置換機制

如何使用 Python 實現 LRU Cache 快取置換機制


Comments