###### tags: `網路基礎` `HTTP`
# [week 4] 網路基礎概論 - HTTP 協定、TCP/IP、API
> 本篇為 [[NET101] 網路基礎概論(搭配 JS 實作練習)](https://lidemy.com/p/net101-js) 這門課程的學習筆記。如有錯誤歡迎指正。
```
學習目標:
知道網路背後大概的運作模式
知道什麼是 Request 跟 Response
知道什麼是 DNS 以及運作原理
知道 HTTP 與 HTTPS 的差異
知道 localhost 跟 127.0.0.1 是什麼
知道 GET 與 POST 的差別
知道常用的 HTTP Header
知道什麼是 API
會使用 node.js 寫出串接 API 的程式
知道 HTTP method 有哪些
知道基本的 HTTP statud code,像是 200、301、400、404、500
```
---
## HTTP 是什麼?
全名是 HyperText Transfer Protocol,中文翻作「 超文本傳輸協定」。
HTTP 是一套網路傳輸協定,為[全球資訊網的資料通訊的基礎](https://zh.wikipedia.org/zh-tw/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE)。也就是說,網頁前端和後端在溝通時,就是透過 HTTP 協定進行。
### 為什麼我們需要 Protocol(協定)?
簡言之,協定就是一個「標準」。協定是為了讓彼此溝通而建立的規範,制定標準以統一格式,如此能夠進行規模化。
## Client & Server
而透過協議溝通的兩端,通常可分為客戶端(Client)和伺服器端(Server)。
![](https://i.imgur.com/EYmEfr8.png)
### 我們如何看到網頁的畫面?
網頁上的資訊,其實就是由許多 request 跟 response 構成。且兩者內容均分成 header 與 body,分別帶著不同資訊:
- header:額外資訊
- body:主要內容
我們可透過瀏覽器的開發者工具、或是 [HTTP 抓包工具 Charles](https://www.itread01.com/content/1547377087.html) 來查看詳細資訊,過程大致如下:
1. 瀏覽器(Client)產生 HTTP「request」傳給 Server
2. Server 經過處理後,回傳一個「response」
3. 再由瀏覽器進行解析(html、css、js 等),將程式碼渲染成我們所熟悉的網頁介面
![瀏覽器](https://i.imgur.com/4pi9zu5.png)
## DNS 域名系統
全名是 Domain Name System,負責將域名轉換成 IP 位置。
- 域名(Domain):即常用的網址。google.com 就是一個域名,類似景點名稱。
- IP 位置:每個主機都有個 IP 位置,是網路溝通的地址。由四個數字組成,範圍是 0 ~ 255。
> 補充:localhost 跟 127.0.0.1 是什麼?
> - `localhost`:是一個域名,對應的的 IP 位址是 127.0.0.1
> - `127.0.0.1` 是回送地址,指本地機,一般用來測試使用
### 以查詢地址為例
1. 如果今天想要抵達 台北 101(域名)
2. 查詢 Google 地址在哪(DNS)
3. 回覆正確地址:台北市信義區信義路五段7號(IP)
> [Google 提供免費的 DNS 伺服器](https://zh.wikipedia.org/wiki/Google_Public_DNS) 8.8.8.8 和8.8.4.4,如此可透過搜尋引擎來蒐集大數據。
### 以 GitHub 網站為例
就是透過 DNS 把 Request URL(github.com) 轉換成實際 IP 位置(13.250.177.223)。
![DNS](https://i.imgur.com/LsnJjwB.png)
### `nslookup` 指令
在終端機輸入 `nslookup '網址'`:可查詢 DNS 伺服器。有可能出現多個結果,代表對應到多個 server。
![nslookup](https://i.imgur.com/wTIOWZF.png)
## 瀏覽器只是一個程式
即使我們沒有瀏覽器,也能夠過其他方式發送 request 跟取得 response。
### 實做一個 Client 端發送 request
利用 Node.js 的一套 library:[request - Simplified HTTP client](https://github.com/request/request),來模擬瀏覽器發送 request 的過程,步驟如下:
1. 安裝套件:`npm install request`
2. 使用方法:複製官網提供的範本,貼到 index.js 裡
```jsx=
const request = require('request');
request('http://www.google.com', function (error, response, body) {
console.error('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
});
```
3. 修改成要發送 request 的域名
```jsx=
const request = require('request');
request('https://github.com/Lidemy/mentor-program-4th-heidiliu2020', function (error, response, body) {
console.error('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
});
```
4. 執行 `node index.js` 可拿到 response
![執行 request](https://i.imgur.com/795sNFV.png)
5. 或是利用 `node index.js > github.html` 導入程式碼,一樣能打開網頁
---
## HTTP Request Method 請求方法
什麼是 `HTTP Method`?在 HTTP 1.1 協定中,定義了八種方法,來以不同方式操作指定的資源。詳細可參考 [HTTP 請求方法- HTTP | MDN](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Methods):
* GET:只應用於取得資料
* HEAD:只獲取回應的 header,但沒有 body。較少使用,通常應用在只想知道 response 資訊時
* POST:用於提交指定資源的實體。通常會改變伺服器的狀態
* PUT:取代原本的整個 request。和 PATCH 類似
* PATCH:修改部分 request
* DELETE:刪除資源
* OPTIONS:會回傳 server 支援哪些方法
* TRACE
* CONNECT
這是為了讓 Server 能夠清楚辨別 request 的目的。
### CRUD 原則
常用的幾個動作分別為:GET / POST / PUT / DELETE,正好對應到資料庫基本操作 [CRUD 增刪查改](https://zh.wikipedia.org/wiki/%E5%A2%9E%E5%88%AA%E6%9F%A5%E6%94%B9):
- Create:新增(POST)
- Delete:刪除(DELETE)
- Read:讀取(GET)
- Update:更新(PUT)
### RESTful
- REST:全名是 Representational State Transfer,中文為「表現層狀態轉換」,是一種設計風格,以語意化且更為嚴謹的方式描述 API
- RESTful:形容以此規範設計的 API
一般的 API 可能會長這樣:
```
新增使用者:POST + /new_user
刪除使用者:POST + /delete_user
查詢使用者:GET + /user_data/:id
更改使用者:POST + /update_user
```
若以 RESTful API 風格開發:
```
新增使用者:POST + /users
刪除使用者:DELETE + /users/:id
查詢使用者:GET + /users/:id
更改使用者:PATCH + /users/:id
```
由此可見 RESTful 的寫法較一致,且語意化更好理解。透過把「動作」藏在 HTTP method 裡面,而有唯一的 URL(`/users`)表示資源位置,藉此統一 API 接口。
> 如果用 HTML 來比喻,寫網頁時我們可以全部使用 div 標籤,但為了幫助理解,通常會使用 li、section、article 等具有語意的標籤,即可從結構看出內容的作用。
參考資料:[API 是什麼? RESTful API 又是什麼? - Medium](https://medium.com/itsems-frontend/api-%E6%98%AF%E4%BB%80%E9%BA%BC-restful-api-%E5%8F%88%E6%98%AF%E4%BB%80%E9%BA%BC-a001a85ab638)
### GET & POST 請求的區別
當中又以 `GET` 和 `POST` 兩種,為最常使用的 HTTP 請求方法。兩者最直觀的區別是「資料傳遞方式」與「安全性」。
#### `GET`:向指定的資源要求資料,類似於查詢操作。
- 資料傳遞方式:將引數由 URL 帶至 Server 端
- 安全性:較 POST 不安全,因為傳遞的引數會在URL上顯示
- 例如:讀取連結或圖片
![GET](https://i.imgur.com/C2MUvS4.png)
#### `POST`:將要處理的資料提交給指定的資源,類似於更新操作。
- 資料傳遞方式:將引數放在 request body 中傳遞
- 安全性:較 GET 安全,適合用於隱密性較高的資料
- 例如:會員登入系統(如下圖所示)
![POST](https://i.imgur.com/K7UVu6f.png)
在最下方夾帶 request 資訊:
![request body](https://i.imgur.com/7dixXCe.png)
### HTTP Status Code 狀態碼
是 HTTP 用「3 位數字代碼」來表示回應狀態,通常以開頭的數字來進行區分。詳細可參考 [HTTP狀態碼- 維基百科](https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81):
#### 1xx:訊息(較少見)
#### 2xx:Success 成功
- `200 OK`:代表成功
- `204 No Content`:伺服器成功處理了請求,沒有返回任何內容(例如發出 DELETE XXX 訊息,回傳 204 代表刪除成功)
#### 3xx:Redirect 重新導向
- `301 Moved Permanently`:資源「永久」移到新位置
- `302 Found`:資源「暫時」移到其他位置
#### 4xx:User error 客戶端錯誤
- `400 Bad Request`:請求語法錯誤、資源太大、請求訊息無效等
- `404 Not Found`:找不到資源
#### 5xx:Server error 伺服器端錯誤
- `500 Internal Server Error`:伺服器出錯。例如搶票時伺服器當機
- `503 Service Unavailable`:由於臨時的伺服器維護或者過載,伺服器當前無法處理請求
參考資料:
1. [[第四週] 網路基礎 - HTTP、Request、Response - Yakim](https://yakimhsu.com/project/project_w4_Network_http.html)
2. [[Day 09] 表單中的 GET 與 POST](https://ithelp.ithome.com.tw/articles/10192095)
3. [GET和POST請求的區別(面試和原理都得知道)](https://www.itread01.com/content/1543571046.html)
4. [http Post 和 Get 差異](https://medium.com/@totoroLiu/http-post-%E5%92%8C-get-%E5%B7%AE%E7%95%B0-928829d29914)
### 實作一個簡易 HTTP Server 端
前面我們已經實作一個 Client 端,也就是利用 request 這個 library 來顯示出來。接下來我們要利用 Node.js 內建 `library:http` 來實作 Server 端:
1. 建立一個檔案 `server.js`,並輸入下列程式碼:
```js
let http = require('http'); // 引用 library: http
let server = http.createServer(handleRequest)
function handleRequest(req, res) {
console.log(req.url)
res.write('hello')
res.end()
}
server.listen(5000); // 監聽 5000 這個 port
```
2. 執行 `node server.js`:不會跑出任何東西。因為沒有 `console.log` 任何東西,所以是正常的。此時程式會一直執行到按 `Crtl + C` 才會退出,符合 server 端必須不斷運行來接收資訊。
3. 在瀏覽器輸入網址 `localhost:5000` 會跑出下列畫面:
![localhost:5000](https://i.imgur.com/PUPfab0.png)
4. 若修改程式碼如下,即可根據「不同網址」給出「不同回應」:
```js
let http = require('http'); // 引用 library: http
let server = http.createServer(handleRequest)
function handleRequest(req, res) {
console.log(req.url)
if (req.url === '/') { // 若網址為根目錄
res.write('welcome!')
res.end()
return
}
if (req.url === '/hello') { // 若網址為 /hello
res.write('hello')
res.end()
return
}
if (req.url === 'redirect') {
}
}
server.listen(5000); // 監聽 5000 這個 port
```
- 輸入網址 `localhost:5000`:回應 welcome
![welcome](https://i.imgur.com/p1PKLHk.png)
- 輸入網址 `localhost:5000/hello`:回應 hello
![hello](https://i.imgur.com/Ay4ut32.png)
5. 再增加 `redirect` 部分和 `res.writeHead(404)`,就完成一個較完整的 Server 端:
```js
let http = require('http'); // 引用 library: http
let server = http.createServer(handleRequest)
function handleRequest(req, res) {
console.log(req.url)
if (req.url === '/') {
res.write('welcome!')
res.end()
return
}
if (req.url === '/hello') {
res.write('hello')
res.end()
return
}
if (req.url === '/redirect') {
res.writeHead(200, {
'lidemy': 'good'
})
res.end()
return
}
res.writeHead(404)
res.end()
}
server.listen(5000); // 監聽 5000 這個 port
```
- 若輸入不存在的網址,就會出現狀態 `404 Not Found`:
![404](https://i.imgur.com/Dy8kyCR.png)
- 若輸入 `localhost:5000/redirect`,會在 Response Headers 會出現 `'lidemy': 'good'`:
!['lidemy': 'good'](https://i.imgur.com/KufqrXq.png)
6. 在之前的狀態碼有提到,若要轉址必須改成 `302 Found`:
```js
if (req.url === '/redirect') {
res.writeHead(302, {
'Location': '/hello' // 指定要轉到哪裡
})
res.end()
return
}
```
7. 輸入網址 `'localhost:5000/redirect`,瀏覽器就會轉址到新位置:
![302](https://i.imgur.com/WX6bq3s.png)
- 若將程式碼改成 `'Location': 'https://google.com'`:輸入網址 `localhost:5000/redirect` 就會直接連到 `google.com`
---
## 淺談 TCP/IP
### 網路的層級
先前提到「從電腦發出 request 到 server」,在溝通訊息這段過程,其實需要經過非常多道手續。因此就有組織將網路標準化,將網路連接過程分成數個階層(layer),每個階層都有特別的獨立的功能。
分層的好處,是只要處理該層級的事情就好,因為每個階層的程式碼可以獨立撰寫,功能也不會相互干擾。
而著名的分層模型主要有兩種:
### OSI 七層協定
由於協定非常嚴謹,較偏向理論。
![OSI](https://i.imgur.com/wLsG4hH.png)
### TCP/IP 四層模型
TCP/IP 是由 OSI 七層協定簡化而來,為目前網路通訊的基礎架構。以下為兩者之間的比較圖與常見的通訊協定:
![OSI TCP/IP](https://i.imgur.com/GFDpM8z.png)
參考資料:[2.4 TCP/IP 的傳輸層相關封包與資料 - 鳥哥的Linux 私房菜](http://linux.vbird.org/linux_server/0110network_basic.php#whatisnetwork_tcpip)
---
## 網路層 - IP 地址
IP 的全名是 Internet Protocol,中文是「網際網路協定」。
可分為 IPv4、IPv6 這兩種協議,兩者最主要差異在於「IP 地址的不同」:
- IPv4:由四個數字組成,範圍是 0~255
- IPv6:用來取代 IPv4,主要是為了解決 IP 地址不夠用的問題,可容納更多 IP 地址
### 各種 IP
我們常聽到的 IP 地址,代表在網路上的地址。在網際網路上,每台電腦之間,是透過 IP 位址來通訊。當中又分為下列幾種類型:
- 固定 IP:不會改變的 IP
理想情況下,是每一台電腦都有一個 IP 位址。固定的 IP 位址,適合架設網站,也因為不會變動,確保使用者能夠連上伺服器。例如:伺服主機、網路設備多使用固定 IP。
- 浮動 IP:每次連上網路時的 IP 位置都會不一樣
一般用戶大多是浮動 IP。浮動 IP 解決了「IP 不夠用」這個問題,因為每次連線的 IP 位址皆不同,也不會輕易被駭客攻擊。
- 虛擬 IP:僅能使用於內部網路,外網連不上該位置
只有在內網才能互相連接,外網找不到該地址,所以內網 IP 位址是可以重複的。但對外網而言,一定會有一個「固定 or 浮動」的 IP 位址。例如:公司使用內網溝通,藉由鎖 IP 位址,可提高安全性。
> 通常以 `192.168` 或 `10.0` 開頭的,都是虛擬 IP。
![虛擬 IP](https://i.imgur.com/TFcsKyY.png)
參考資料:[浮動 IP 與 固定 IP 有何不同?? 各有何優缺點??](https://ithelp.ithome.com.tw/questions/10000796)
---
## Port 連接埠(端口)
在應用層當中,每台電腦主機 IP 位置(localhost),可能對應到不同應用程式。例如:HTTP、FTP、信件收發等服務。
而 Port 扮演網路通訊的端點,用來區分不同功能。如此即可辨認出,該連線要對應到哪個應用程式。
- 例如實作 server 端提過的 `server.listen(5000)`:代表監聽 `5000` 這個 Port,輸入網址 `localhost:5000` 即可連到這個服務
如果沒有輸入 Port,會有一些預設或常用值:
- HTTP 80:說明用於網頁瀏覽
- HTTPS 443
- FTP 21
- 「測試」常用:3000、4000、8080、8000 等冷門 Port
參考資料:[[網路管理]常用 port 說明](https://dotblogs.com.tw/box5068/2011/01/19/20901)
---
## 傳輸層 - TCP & UDP
傳輸層(Transport Layer)協定提供不同主機之間的資料傳輸及控制。根據不同需求,又分為「可靠的 TCP」和「速度較快的 UDP」兩種協定:
### TCP
- 全名:Transmission Control Protocol 傳輸控制協定
- 是一種可靠的資料傳輸,因此大部分的網路協定都是建立在 TCP 上面
- 透過「三次握手」確認建立一個連接:
![](https://i.imgur.com/PHSLrDe.png)
```
若以「傳紙條概念」比喻三次握手:
小明:安安,在嗎?
小美:在阿,你好。
小明:收到,太好了!
```
### UDP
- 全名:User Datagram Protocol 用戶資料包協定
- 流量是不受規範的,需要快速、重複傳送資料的情況會使用 UDP,相對而言也較不穩定
- 例如:視訊功能
參考資料:
1. [TCP協議 - Wikipedia](https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE)
2. [什麼是OSI的7層架構?和常聽到的Layer 7有關?](https://ithelp.ithome.com.tw/articles/10000021)
## 小結 - 從傳紙條概念看 TCP/IP
參考 [Huli - 從傳紙條輕鬆學習基本網路概念](https://medium.com/@hulitw/learning-tcp-ip-http-via-sending-letter-5d3299203660),我們能夠透過傳紙條的例子,來試著理解網路通訊概念:
- 應用層(HTTP / FTP):傳輸的資料內容
- 傳輸層(TCP / UDP):傳輸方式
- 網路層(IP):傳輸地址
- 實體層(網路電纜):實體傳輸
![傳紙條概念](https://i.imgur.com/oJPxLiH.png)
---
## 淺談 API
> 可參考 [Huli - 從拉麵店的販賣機理解什麼是 API](https://medium.com/@hulitw/ramen-and-api-6238437dc544),或是這部影片:[什麼是 API?](https://www.youtube.com/watch?v=zvKadd9Cflc&feature=emb_logo)來瞭解。
全名是 Application Programming ==Interface==,中文是「應用程式==介面==」。簡言之,就是方便溝通、交換資料的管道。
API 是應用程式、裝置之間資料的交換,但不一定要透過網路才能有 API,例如:
- 軟硬體廠商的 API:USB 與 電腦交換資料
- 作業系統裡的 API:讀取、傳輸及寫入等等電腦上的操作
### 而對於網頁來說就是 Web API
- Web API:是基於 http 協定下運作的 API,代表透過網路進行資料交換
以下為實際的運用例子:
- 會員登入系統:社群連結註冊登入(例如:設置 FB 或 Google 登入按鈕)
- 社群嵌入:分享、留言版、按讚按鈕、影音(例如:[Facebook Graphic](https://developers.facebook.com/docs/graph-api?locale=zh_TW))
- 資料嵌入:[Yahoo 氣象](https://developer.yahoo.com/weather/?guccounter=1&guce_referrer=aHR0cHM6Ly95YWtpbWhzdS5jb20vcHJvamVjdC9wcm9qZWN0X3c0X05ldHdvcmtfQVBJLmh0bWw&guce_referrer_sig=AQAAABbKqoTfIecNtugMpoKKf5L53XwIJA_l1zPrrJUmPD2Yh1N-rcXbaJhbnPQAKW-UbnT_NJhQX_4isY374OTvbtR1q91DdGlYOK4qSQyQqr2bkr5XMPQuo8bQT874RICV6IF2sHsr2yp84UXMV4549R1aXFjHNt9lifII3YdTvbAY)、[Google 地圖](https://developers.google.com/maps/documentation/timezone/start?hl=zh-tw)、[Tweet timelines](https://developer.twitter.com/en/docs/tweets/timelines/overview)
- [PokéAPI](https://pokeapi.co/):拿到寶可夢的資訊
- [假圖 API](https://picsum.photos/):隨機產生出圖片
透過 API 能夠達到省時、便利、營利等優點,因此我們也可以說,API 是一個「能讓生產者與消費者雙方溝通的介面」。
## 實際串接 API
### 串接的前置作業
1. 準備 Client 端(發出端):使用 node.js [library request](https://github.com/request/request) 實作一個 Client 端
2. 提供 API 的網站(接收端):[Regres](https://reqres.in/) 是一個提供 API 測試的網站
3. 能夠發出 request 的工具:終端機
### 實戰演練
### Step1. 實作一個 Client 端
與先前「實做一個 Client 端發送 request」操作方式相同,是利用 [library request](https://github.com/request/request) 來模擬瀏覽器發送 request 的過程。
- 新增 index.js 並貼上官網提供的[範本](https://github.com/request/request#super-simple-to-use):
```js
const request = require('request');
request('http://www.google.com', function (error, response, body) {
console.error('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('head:', response.headers);
console.log('body:', body);
});
```
### Step2. 串接 [Regres](https://reqres.in/api/users) API
將範本中的 google 網址改成 `https://reqres.in/api/users`
### Step3. 用 node.js 運行
在終端機輸入 `node index.js`,即可獲得 Regres API 所提供的資訊:
![運行](https://i.imgur.com/DWirKxl.png)
這樣其實就完成了簡單的 API 串接!透過丟一個 request 到網站,我們能夠獲取想要的資訊。
## 學會查看官方文件
而透過 [request 文件](https://github.com/request/request#forms),我們能找到不同的 `request.METHOD()`,瞭解如何使用各種方法:
```js
request.get(): Defaults to method: "GET".
request.post(): Defaults to method: "POST".
request.put(): Defaults to method: "PUT".
request.patch(): Defaults to method: "PATCH".
request.del() / request.delete(): Defaults to method: "DELETE".
```
### 情境一:查詢不同使用者資料
- Method:GET(預設方法)
- url :`https://reqres.in/api/users/<userID>`
- 透過 [Reqres 官網](https://reqres.in/)可知,更改 「Request 網址結尾」可獲得不同使用者資訊,以 `/api/users/2` 為例:
方法一:直接將網址改成 `https://reqres.in/api/users/2`
![id 2](https://i.imgur.com/JiCCL5b.png)
方法二:使用 node.js 內建 library `process`
這個方法是利用 `process.argv` 達成帶入參數,我們可以先用 `console.log` 來查看 `process.argv` 是什麼:
```js
const process = require("process");
console.log(process.argv);
```
![array](https://i.imgur.com/HrpOUWq.png)
會發現 `process.argv` 其實是一個陣列,利用 `process.argv[2]` 就可以拿到我們需要的參數:
```js
const request = require('request');
const process = require('process');
request(
'https://reqres.in/api/users/' + process.argv[2],
function (error, response, body) {
console.log('body:', body);
}
);
```
輸入 `node index.js 2`,就可獲得 `id: 2` 的使用者資料:
![node index.js 2](https://i.imgur.com/dRHS9rO.png)
### 情境二:新增使用者資料
- method:POST
- url :`https://reqres.in/api/users/`
- 到 request library 的 Github 頁面,[點選 forms](https://github.com/request/request#forms),填入相關程式碼來新增資料至 reqres
```js
const request = require('request');
request.post(
{
url: 'https://reqres.in/api/users',
form: {
name: 'Heidi',
job: 'web engineer'
}
},
function (error, response, body) {
const json = JSON.parse(body) // 轉成 JS 物件
console.log(json);
}
);
```
成功新增使用者資料:
> 註:這裡的操作只是測試用,並不會真的新增資料到網站。
![新增使用者資料](https://i.imgur.com/SOHZu0h.png)
### 情境三:刪除使用者資料
- method:DELETE
- url :`https://reqres.in/api/users/<userID>`
```js
const request = require('request');
request.delete(
{
url: `https://reqres.in/api/users/2`, // 將使用者 id 帶入
},
function (error, response, body) {
// console.log(JSON.parse(body));
// 因為是刪除,所以不會回傳東西,因此轉印狀態碼
console.log('status code:', response.statusCode)
}
);
```
回傳 `204`,代表成功刪除使用者資料:
![delete](https://i.imgur.com/JHOyAUo.png)
### 情境四:修改使用者資料
- method :PATCH
- url :`https://reqres.in/api/users/<userID>`
```js
const request = require('request');
request.patch(
{
url: `https://reqres.in/api/users/2`,
form: { // 傳入要修改的資料
name: 'hello'
}
},
function (error, response, body) {
console.log(JSON.parse(body))
}
);
```
成功修改使用者資料:
![patch](https://i.imgur.com/HXbVmTk.png)
### 綜合應用
Base URL: https://lidemy-book-store.herokuapp.com
| 說明 | Method | path | 參數 | 範例 |
|--------|--------|------------|----------------------|----------------|
|獲取所有書籍| GET | /restaurants | _limit:限制回傳資料數量 | /restaurant?_limit=6
|獲取單一書籍| GET | /restaurants/:id | 無 | /restaurant/12
|刪除書籍 | DELETE | /restaurants/:id | 無 | 無
|新增書籍 | POST | /restaurants | name: 書籍名稱 | 無
|更改書籍 | PATCH| /restaurants/:id | name: 書籍名稱 | 無
```js
const request = require('request');
const API_ENDPOINT = 'https://lidemy-book-store.herokuapp.com';
request(`${API_ENDPOINT}/books?_limit=10`, (error, response, body) => {
// 判斷 status code 是否為 2 開頭:偵測回傳是否成功
if (response.statusCode >= 200 && response.statusCode < 300) {
let books = '';
// try catch:偵測處理資料這個動作是否出現錯誤
try {
books = JSON.parse(body);
// 列出前十筆資料
for (let i = 0; i < books.length; i += 1) {
console.log(`${books[i].id} ${books[i].name}`);
}
} catch (err) {
// 若 response 不是一個合法的 JSON 字串,會回傳錯誤
return console.log(err);
}
}
});
```
### 補充:如何在請求帶入資料
在發出 request 時,我們其實能透過兩種方式來帶入資料:
1. Request header:把資料放入 header
- 通常會放層級較高、具有機密性的資料,例如身分驗證
2. Query-string parameter:把資訊放在網址列結尾
- 由結尾的問號開始,是 KEY / Value 的組合
實際例子可參考[Twitch API v5](https://dev.twitch.tv/docs/v5) 文件,同樣支援這兩種方法:
1. Request header (`Client-ID: XXXXX`)
2. Query-string parameter
(`https://api.twitch.tv/kraken/users/44322889?client_id=XXXXX`)
因此在串接之前,必須先確認該 API 支援哪種方式來發出請求。
---
## 資料格式 XML & JSON
在 API 實戰中的 response 資料其實就是「JSON 格式字串」。在談到如何整理資訊之前,先來介紹常用的資料格式:
### XML
- 全名為 Extensible Markup Language,中文為「可延伸標記式語言」
- 和 HTML 非常類似,均屬於 Markup Language(標記語言),內容用`前後標籤`包起來。
- 例如:`<firstName>John</firstName>`
- 缺點是檔案較大、不易閱讀,因此現代開發較少使用
### JSON
- 全名為 JavaScript Object Notation,中文為「JavaScript 物件表示法」
- 為現代最普遍、常用的資料格式
- 格式容易理解,且相容性高,許多程式均支援讀取或修改
- 資料格式看似 JavaScript 物件,但需注意以下幾點:
1. 回傳值的型態是「字串」
2. `key` 值要用雙引號 `"key"` 包起來
3. 支援許多資料格式:`[array]`、`{object}` 等;但 `value 值` 不能放 function
4. 整個 JSON 格式字串不能使用註解
- 相關函式
1. `JSON.prase(<JSON>)`:將 JSON 格式字串轉成物件
2. `JSON.stringify(<object>)`:將物件轉成 JSON 格式字串
參考資料:
1. [你不可不知的 JSON 基本介紹](https://blog.wu-boy.com/2011/04/%E4%BD%A0%E4%B8%8D%E5%8F%AF%E4%B8%8D%E7%9F%A5%E7%9A%84-json-%E5%9F%BA%E6%9C%AC%E4%BB%8B%E7%B4%B9/)
2. [JSON Editor online](https://jsoneditoronline.org/):可將得到的資料轉成 JSON 格式
### 實際來處理 JSON 格式字串
同樣以串接 Reqres API 為例。得到 response 就是「JSON 格式的字串」,但這種資料並不易閱讀,也無法直接使用。
因此使用內建函式 `JSON.parse()` 做處理,就會轉成「JS 物件」:
```js
const request = require('request');
request(
`https://reqres.in/api/users/2`,
function (error, response, body) {
console.log('原始格式,JSON 格式的字串----------');
console.log(body);
console.log('轉成 JS 物件--------------------');
console.log(JSON.parse(body));
}
);
```
![json](https://i.imgur.com/rxiOXN2.png)
如此就可以「物件」方式來取出想要的資料:
```js
const request = require('request');
request(
`https://reqres.in/api/users/2`,
function (error, response, body) {
const json = JSON.parse(body) // JSON 物件
console.log(json.data.first_name); // 印出 Janet
}
);
```
反之,若想把「JS 物件」轉成「JSON 格式字串」,可以用: `JSON.stringify(<object>)`
---
## 必學指令 `curl`
> 在 [curl 官網](https://curl.haxx.se/)下載,安裝方法可參考 [Day14 - cURL 工具](https://ithelp.ithome.com.tw/articles/10185923),即可在終端機使用。
curl 做法非常簡單,只要在終端機輸入指令就可支援發 HTTP request 、下載及上傳檔案的功能,基本格式為:
```js
curl [options] [URL...]
```
### 預設是 `GET` 方法
以使用 curl 能夠發 request 到 google 首頁為例:
- 輸入 `crul 'http://www.google.com'`:即可下載該網頁程式碼
- 或輸入 `crul '網址' > google.html`:可將回傳值導向其他檔案
![](https://i.imgur.com/RbyGVvV.png)
參考資料:
1. [linux指令 curl指令详解](https://blog.51cto.com/doiido/1564631)
2. [Linux Curl Command 指令與基本操作入門教學](https://blog.techbridge.cc/2019/02/01/linux-curl-command-tutorial/)
---