---
tags: backend
description: 6/16後端會議
---
# 二、06/16 HTTP protocal(承儒)
## HTTP / 1.1
* HTTP (Hypertext Transfer Protocol) 超文本傳輸協定,主要是架構於 TCP/IP 之上的 應用層,為 無狀態 的 請求-回應 通訊協定。
* HTTP 由最初的 頁面、媒體傳輸,發展為今日 Web 應用的根基,許多 APP、嵌入裝置、軟體…,皆依此取得服務,因此,也成了大部分開發者的必備技能。
* 基本的網路傳輸資料模式,原本有上對下,傳過去由下到上解密
---
## TCP/IP model

---
Topic
1. Interface
2. URL,Resource
3. Message,payload
4. Request/Response
5. Example
6. Homework
---
## Interface
### Server vs Client
* 客戶端 (Client),指用來發起連線,建構並發出 一或多個 請求 (request) 給伺服端,以傳達所需意圖者。
* 伺服端 (Server),是指監聽 (listen) 與接受 (accept)/拒絕 (reject) 連線,並送回 一或多個 回應 (response),以服務客戶端者。
* 兩者只是一個對應的角色
* Client、Server 僅指特定連線的相對角色,一個程式可能有時做為 Client,某些時候為 Server。
* 大部分應用程式,你玩的遊戲都有兩者的功能,會接收跟傳送訊息
* Http只是一個應用層,不具有傳輸之用
:::info
* 通訊協定
* 只是一個介面,他的實作依賴client和server如何去定義訊息(自定義)
* 如果網頁都用http的話,那網站傳輸資料也都要使用http的通訊協定
:::

## URI,Resource,Representation
:::info
「HTTP 透過 Client 發送 請求 (Request),Server 送出 回應 (Response) 以進行互動。」
:::
* HTTP通過Client發出請求 (Request),Server回傳Response,發出請求的目的就是去找Server的Resource,可能是一個檔案或是database,任何一個可被實體化出來的都可稱之為一個Resourse
* ==HTTP是一個通訊協定,定義我們與Resource互動的介面==
**那 請求 的 目標 (target) 與 目的 (purpose) 是?**
### Resource
:::info
可能是一個檔案、資料庫的查詢結果、另一群資源的集合或任何能被命名的資訊,都是 — 資源(resource)。
:::
**HTTP 沒有限制資源的種類,只定義與資源交互的介面。**
### URI (統一資源識別符 )
```
http(s) URI =
"http(s):" "//" path [ "?" query ] [ "#" fragment ]
Ex:
https://echo.paw.cloud:443/hello/world?age=24&gender=female#Uehara
```
* 統一資源識別符 (Uniform Resource Identifier, URI)
* 即「辨識 資源 的方式」
* 常見如同 10.33.41.126 就是一個內網網址,只有瀏覽器是沒用的,還需要透過 HTTP URI,來指定 目標資源。
* 按照 URI 標準,上面的例子實際上是一個URI,包含了以下三者 :
* 介面 (http)
* HOST (echo.paw.cloud)
* 路徑 (/hello/world)
* URL就是一種URI
#### 路徑選填
1. ==":"port==
* 連接埠 (port),若無特別指定,預設是 80 port,
* 若是 https 則預設為 443 port
* 欲指定其他埠號需須根據 Server 之個別規範。
2. ==“?” query==
* 查詢 (query), 以「?」開始
* 用 key=value 的鍵值對接著
* (中間別忘了加 =)
* 若還有其他 query ,則通常以「&」做為區隔
* 譬如: ?age=24&gender=female
* 代表的可能是,我想在這個網站 (如果有支援)尋找:
* 『年紀 24 且 性別是女性 』。
* 開發時,記得處理 query 字串 (尤其是 客戶端應用)
* 以免送出保留字元或錯誤的編碼。
* 沒處理的話容易受到SQL injection,這也是最常見的攻擊方式
* 安全性問題在於query的設定不完整
* 例如:敏感字元沒有篩選,就能直接利用敏感字元攻擊
3. ==“#” fragment==
* 片段 (fragment),是輔助資源,通常為主要資源的一部份或子集
* fragment,是由 Client 端 (通常為瀏覽器) 處理的部分,並 不會傳遞給 Server !
* 因此,HTTP 規範所說的 — 目標 URI (target URI),不包含 片段 (fragment) 部分。
#### HOST(主機)
* 包含 網域名稱 與 IP 位址 (e.g., google.com、172.217.27.142)
* 若使用的是經註冊的網域名稱 (Domain Name)
* 例: 「example.org」、「NotFalse.net」…
* 則會先透過 名稱解析服務 (ex: DNS),找到 Server 的 IP 位址,並藉此訪問:
* google.com等等網址都是透過DNS轉成一個domain
* 我們要訪問google就會先通過DNS轉譯,得到一個IP

:::success
決定了 請求目標 後,HTTP 透過可靠的 (reliable) 傳輸/會話層 (e.g., TCP),
進行連線 (connection) 以 交換訊息 (Exchange Message)。
==那現在就要來看看怎麼處理Message的部分囉 !==
:::
### Message,Payload
* 請求範例
```
GET /31/fetch-api HTTP/1.1
Host: notfalse.net
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-encoding: gzip, deflate, br
accept-language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
////body////
```
所有訊息格式都是:

* CRLF (carriage return followed by line feed),是網際網路 (Internet) 嚴謹的 換行 (newline) 標準 ,也就是鼎鼎大名的:
* CR = 回車 (Carriage Return),又稱 歸位 => \r
* LF = 換行 (Line Feed) => \n
### Request
* GET or POST 方法 (method)、空白 (space, SP)、請求目標 (request-target)、再一個 空白 (space, SP)、HTTP 版本 (HTTP-version)
* GET / HTTP/1.1
* 方法GET是區分大小寫的
* HTTP/1.1 -> HTTP版本
對應上面的顏色範例:

### Response
* HTTP 版本 (HTTP-version)、空白 (space, SP)、狀態碼 (status-code)、再一個 空白 (space, SP)、原因短語 (reason-phrase)、以及 CRLF (carriage return followed by line feed)。
* 請求/回應 訊息,語法上格式相同,
* 但 內容 與 訊息主體 (message body) 的規則不同。
* 正確或錯誤訊息都統一顯示在status code
* 狀態碼其實是後端自己定義的

#### header-field
表頭 (header),又譯為 首部、檔頭、標頭,
是由 0 或多個 表頭欄位 (header-field) + CRLF 組合而成。
類似程式語言 函式/方法的 參數 (parameter)。
例如,你對一服務生指定 目標資源 — — 牛排,
而 要幾分熟、幾份餐具、點心什麼時候上…,
這種 附加的重要資訊,即為 表頭 (header)。
:::info
資訊的附加重要資訊
之後的訊息如果是用JSON,就是在這邊定義
寫出來要空兩行,以顯示與下方的body的區隔
:::
#### message-body訊息主體
訊息主體 (亦稱Body),就像包裹的箱子,是 訊息 (message) 中 乘載 資料的地方。
:::info
其實資料格式都可以隨便亂傳,只是server會不會回應你而已
這邊的資料大小都是用位元組去運算
:::
==只是這樣子怎麼知道要傳什麼格式呢?==
### Representation 資料表示
:::info
表示 (representation) (確切來說為 資源 的 表示),旨在透過協議容易傳達的格式 (位元組 bytes),並伴隨 元資料 (metadata),以反映出 資源「現在」「過去」或「預期」的 『狀態 (state)』。
:::
* 元資料->你傳送時要傳給server看,看看content type有沒有符合
* 表示 (representation) 的 元資料 (metadata),通常包含了:
* 內容類型 (Content-Type)
* 內容編碼 (Content-Encoding)
* 內容語言 (Content-Language)
* 內容位置 (Content-Location)...
#### content type 內容類型
Content-Type 表頭欄位,是指相關 表示 的 媒體類型 (Media Type),用於定義 資料格式 (data format),以供接收者以相應的方式處理。
* 例如,下面的是用JSON和utf-8,之後的就不能使用 plain-text
* (plain-text就是一般的字,連括弧都沒有)
* (JSON是用字串傳,其實都是用字串傳,只是它有自己個格式)

---

存取某個API時,非常重要
* 其中「application/x-www-form-urlencoded」or「application/x-www-form-urlencoded; charset=utf-8」,普遍用於 HTML 中的 POST 表單 (e.g., 提交帳號密碼),是 類似百分比編碼 的==鍵值對==形式。
* 例如:「name=%E5%8B%9D&password=9487」
* 就像encode,"%E5%8B%9D"就是你打的帳號,編碼之後再遞交
* Google在搜尋時,就可用非英文搜尋,只是他會再幫你encode。
:::success
一個 資源,可能具有不同的 表示 (representation) 方式 (不同的類型、編碼 或 語言…),如「使用者資料 」概念,我們可以用 JSON 表示,也可以表示為XML。
:::
```javascript=
{
"id": 9487,
"name": "Jason",
"speciality": "sleep",
"gender": "male",
"blog": "NotFalse 技術客",
"blog_url": "https://NotFalse.net"
}
```
---
也可以表示為XML
```xml=
<id>9487</id>
<name>Jason</name>
<speciality>sleep</speciality>
<gender>male</gender>
<blog>NotFalse 技術客</blog>
<blog_url>https://NotFalse.net</blog_url>
```
#### 內容協商機制
:::info
不同的 使用者 或 使用者代理 對於 表示,可能具有不同的功能,特徵或偏好。
而 Client 與 Server 如何協調,以傳輸最理想的 表示 的機制,
則稱為 — — **內容協商 (Content Negotiation)。**
:::
* 缺點:霸道,我們只要這幾個,但如果server不提供,他就會不提供或是回傳錯誤的資源。
* 使用者今天要Gzip就回傳Gzip,要html就回傳html
---
內容協商 (Content Negotiation) 主要有三種模式:
* 伺服端驅動 (server-driven)
* 使用者代理驅動 (agent-driven)
* 代理驅動 (proxy-driven)
---
#### 伺服端驅動 (server-driven)
* 內容協商 使用最頻繁的模式,又稱 主動協商 (Proactive Negotiation) 或 搶先式協商 (preemptive negotiation)。
* 是指 使用者代理 (User Agent) 在請求中發送 期望的 偏好表示資訊,使 Server 根據該偏好資訊,使用適當的演算法,以 選擇 (select) 優選的表示。
* 結果可能事與願違
* 輕則回應非預期的表示
* 重則回應 客戶端錯誤 (e.g., 415 Unsupported Media Type, 406 Not Acceptable)。

---
#### 使用者代理 (User Agent)
* 代送請求或幫你處理的agent,上面其實是表明自己的身份,看server那邊有沒有bang掉。
* 這算是必要,你普通查詢的時候不會發現,但可能你寫爬蟲時,他們就會很嚴格的要求user-agent
* 藉由顯示的 協商欄位,指明對表示的 期望選項:
* Accept
* 相對於剛剛的content type是傳送,accept就是接受
* Accept-Charset
* 對映於 Content-Type 的 charset 參數,隨著對 UTF-8 良好的支持,大部分通用的使用者代理,並 不會 發送 Accept-Charset 欄位。
* Accept-Encoding
* 對映於 Content-Encoding 欄位,也可以使用 空值 指明不進行編碼 (no encoding)。
* Accept-Language
* 對應於 Content-Language 欄位
* 可以設定 想要資料的優先順序,0.8>0.3,先取0.3的
* 傳送的重點是Accept 跟 Encoding ,中間預設都是utf-8
### Requeset / Response

* 那麼,除了在 網址列 中輸入 URI,還有什麼方式送出 HTTP 請求呢?
* 可以用 JavaScript 的 XHR、jQuery、Fetch API(幫我們實作http的工具)
* 或者 GET vs. POST 介紹的 HTML 表單。
* 還有 發送請求 的 Postman、Insomnia 等 GUI 工具,
* 甚至 以 Socket 實作 HTTP Client!
## Example
```
API 網址: https://www.thef2e.com/api/{router功能路由}
```
---
```
[API]: /isSignUp
[方法]: post
[參數]:
{
"email": "hexscholl@test.com"
}
[成功回應]
{
"success": true,
"message": "報名成功!",
"nickName": "gonsakon",
"timeStamp": 1527035405000,
"Certificate": "獎狀網址"
}
[失敗回應]: 未報名成功
{
"success": false,
"message": "此 Mail 尚未報名"
}
[失敗回應]: 參數錯誤
{
"success": false,
"message": "email 參數未提供"
}
```
---
```javascript=
POST /api/isSignUp HTTP/1.1 \r\n
Host: www.thef2e.com \r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0 \r\n
Content-Type: Application/json \r\n
\r\n
{
"email": "hexscholl@test.com"
}
```
---
```
[API]: /signUpTotal
[方法]: get
[成功回應]
{
"success": true,
"total": 899
}
```
---
```javascript=
GET /api/signUpTotal HTTP/1.1 \r\n
Host: www.thef2e.com \r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0 \r\n
\r\n
//////空der//////
```
---
### 重申:
* http沒有傳輸功能
* 不是 傳輸資訊 用的 protocal
* 只是在傳輸層 TCP/IP 協定上的應用層協定
* ==所以,並不需要HTTP也能傳送資料,只是傳送的資料可否被 **識別**==
* 傳輸實際上是給TCP和IP兩層還有你的實體網路做的,http比較像是要怎麼去標記我們的訊息,讓我們的資料能被識別。
* TCP IP 實體網路都解決後,才會到http的應用。
* 但,現在都有工具讓你不用硬幹http
---
以上就是簡單的小示範,HOST在真實的環境中是不用填上的,會自己找然後帶入,也就是無法在 request 中自行修改,現在教另外一個工具叫做 POSTMAN ,可以測試 API 的小工具。
### POSTMAN DEMO
* 如果是get,直接貼url,就會回傳你JSON
* form-data 也是 raw ,只是 encode 的方式不同
* form-data / raw / JSON
* server那邊要定義你要接的資料格式,前端也要傳這樣的格式下去給server處理,但你也可以在content-type那邊寫JSON但傳一堆plain-Text,然後server也可以寫好JSON,但不處理JSON,回傳你一堆plain-Text。
==主要是前後端溝通要溝通好==
* Endpoint
* 網路傳輸的中點,只要可以被傳輸,主要傳輸資料的就叫endpoint
==其實網站本身也是API,只是回傳的是資料還是網站==
* GET & POST
* POST是你要先傳一些資料過去,通常用於表單,如帳密
---
## 了解了這些還有呢 ?
Header 的更多注意事項(order/duplicate),Message Routing(Proxy/Tunnel/Gateway),Cache ...等等都是非常重要的技能。
更多資訊/參考資訊 : https://notfalse.net/33/http1_1
---
## Homework
使用 POSTMAN 來測試 幾個 API 是否可用。
---
題目一 : HTTP GET URL : https://120.126.136.19:7089/get ,需要附加標準HTTP protocal 內容,不須攜帶任何Body。
應該接收到的回應為 { "message" : "Hello World!"}
---
題目二 : HTTP POST URL : https://120.126.136.19:7089/post ,除了需要附加標準HTTP protocal 內容,並額外包含以下的 Header => Content-Type : application/json ,並且 Body 需要有 { "StuId" : "你的學號 英文名字"}
應該接收到的回應為 { "message" : "Hello! 學號 名字"}
---
# 同場加映: Express 周杰
## 什麼是 Express? 為什麼要用 Express?
:::info
Express 是目前最穩定、使用最廣泛開發框架,並且是==Node.js 官方唯一推薦的 Web 開發框架。==
Express 除了為 HTTP 模組提供了更高階的接口外,還實現了許多功能,其中包含:路由控制、模板解析支持、動態視圖、用戶會話、CSRF 保護、靜態文件服務、錯誤控制器、訪問日誌、緩存、插件支持。
==特別在此註明,Express 不是一個無所不包的全能框架==,像 Rails 或 Django 的那樣實現了模板引擎甚至 ORM(Object Relation Model,對象關係模型),它只是一個輕量級的框架,多數功能只是對 HTTP 協議中常用操作的封裝,更多的功能需要插件或整合其他模組來完成。
:::
### 以周杰的router為例
流程:
* 使用Express首先要創建一個實體,設一個變數
* app.use->所有東西在使用的時候都會經過這一道手續
* 意思是寫的任何的網址api都會經過這道手續,後續才會接
* 接著會有階層的關係,你可以先寫一個根目錄去引用你想要的目錄子集
```javascript=
//server.js
const express = require('express');
const router = require('./routes/index');
//去看index裡面有什麼
...
const app = express();
...
app.use('/api',router);
//添加router中間件
//先寫一個api根目錄
//router接後續想要的子集
---
//index.js
const express = require('express');
const articles = require('./articles');
const videos = require('./videos');
const router = express.Router();
//路由中間件
router((req, res, next) => {
console.log('this is a api request!');
//任何路由訊息都會執行這句,讓我們知道這件事有成功
next();//把他交給下個middleware
//middleware的註冊順序是按序執行
});
//再分一個子集下去,目錄再目錄的概念
router.use('/articles', articles);
router.use('/videos', videos);
---
//articles.js
const express = require('express');
const Articles = require('../db/models/articles');//引入模組
const router = express.Router();
//這邊取用不是用/
//而是用 /api/articles
//因為子層有定義相對的根目錄
router.get('/', (req, res) => {
res.json([{
id: 0,
title: '前端好難',
content: '嗚嗚嗚真的好難TAT',
}, {
id: 1,
title: '後端好難',
content: '嗚嗚嗚真的好難TAT',
}, {
id: 2,
title: '學完 redux後發現還有 graphql',
content: 'asd',
}])
});
//router使用get寫在這根目錄,就是要回傳這些東西
modules.exports = router
```
```javascript=
//前端
...
fetch("http:/localhost:3001/api/articles")
.then(res => res.json())
.then(data => {
console.log("data: ", data)
this.setState({
articles: data,
})
})
...
```
:::success
而前端在fetch的時候,也就寫同樣的路徑,也就是後端寫好這個路徑後,前端就可以接。
:::
後續就是再加有的沒的套件,像是JWT等等。
更詳細的參考資料:
[使用 Node.js + Express 建構一個簡單的微博網站](https://cythilya.github.io/2014/11/23/nodejs-express-microblog/)