[Tech] HTTP/HTTPS/RESTful ?

Jack Huang
A Good Guy
11 min readJul 17, 2019

--

Hi~大叔我又來惹~
最近幾天都一直在弄 paper 的東西,現在總算有點時間可以好好整理一些筆記。整理到一半就看到了以前有對 RESTful 整理過相關的概念,現在就正好拿出來聊聊。

因為對於許多人而言大多只存有”有些網頁會是 HTTP 有些是 HTTPS” 的概念,因此在提到 RESTful 前就一定要先提什麼是 HTTP 以及 OSI model。

The Open Systems Interconnection Model breaks down network communication into seven layers. These layers are useful for identifying network issues.

在網路通訊的世界中,因為存在了許多資料的交換,每種資料都有不同的意義,因此為了讓在網路上的資料流進行資訊交換時能區分彼此,網路的資料傳遞存在不同的 layer,而這些 layer 主要分類的依據是資料如何被處理,因此實質上會根據使用者的行為決定資料轉換到哪一個 layer。

其中的第 7 層 Application Layer 是大部分使用者會接觸到的 Layer

每層中都會定義不同的傳輸協定(protocol)用於分辨在同一個 layer 下不同的資料訊息交換,我們就不一一細講每個 Layer 的作用(如果要講可能就要直接另外開一個主題來討論),先 focus 在第 7 層的 Application Layer。
在 Application Layer 這層與使用者最為接近,使用者大多能透過不同的應用來直接的與 Application Layer 的資料作互動,而在此層的應用大部分會基於主從式架構P2P 實作。像是 Chrome、Firefox 等 browser 屬於在 Application Layer 實作的一種應用,它透過發送符合 HTTP/HTTPS protocol 的 request 封包獲取相對應的資源渲染在畫面上。
我們的 HTTP/HTTPS 也落在 Application Layer 上。但是實際在 Application Layer 中會根據不同的用途存在各種不同的傳輸協定,像是 FTP 用於檔案傳輸、SMTP 用於郵件傳輸、SSH 用於用戶與伺服器連線等。

講到這邊你大概會對網路傳輸有點概念,那麼究竟什麼是 HTTP/HTTPS 呢?

HTTP/HTTPS 這種 protocol 便是定義資料在傳輸時的規範

最初設計 HTTP protocol 是為了提供一種發布以及接受 HTML 的方法,通過 HTTP protocol 請求的源由統一資源識別元(Uniform Resource Identifiers,URI)來標示,並且存在 header 以及 body 個別存放 HTTP 封包的資訊以及內容。也就是說當一主機接收到封包時,因為不同的 protocol 都會有一定的格式,因此主機可以透過封包的格式(header)辨別該封包屬於哪一種協定的封包,並將該封包在轉交給特定服務進行接下來的操作。

而 HTTP 的 protocol 中並沒有定義雙方身份驗證的步驟,任何人都可以發送請求的情況下 server 無法辨別每個請求都會理所當然的做出 response,因此存在基於 HTTP 之上,加入了第三方(CA)憑證驗證身份加強安全傳輸的部份。接下來將針對 HTTP 的格式稍作解釋

HTTP/1.1 — 訊息格式 (Message Format)

HTTP 透過統一的介面 (uniform interface),Client送出請求 (Request), Server 送出回應 (Response)。
那實際上請求與回應到底是在傳什麼?

很簡單,實際上就是在傳字串

HTTP 定義了 Client 以及 Server 之間互相傳字串的格式

不過實際上 HTTP 存在好幾個版本,在進行 HTTP 請求時客戶端會告知伺服端自己所使用的 HTTP 版本,而伺服端接收到以後便會以相同或是更早的 HTTP 版本回應 (HTTP 有作向下兼容)

【client request http packet format】

【server request http packet format】

上面兩張圖為 HTTP 所定義的訊息模板 (Model),HTTP 訊息由 2 部分所组成:

  1. 請求頭 (Request Header)
  2. 請求正文

而下面則是一個 client 請求的範例

GET/sample.Jsp HTTP/1.1
Accept:image/gif.image/jpeg,*/*
Accept-Language:zh-cn
Connection:Keep-Alive
Host:localhost
User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0)
Accept-Encoding:gzip,deflate
username=jinqiao&password=1234

請求方法 URI 協議版本

GET/sample.Jsp HTTP/1.1

以上代碼中

  1. “GET” 代表該請求是使用 GET 的請求方法
  2. “/sample.jsp” 則代表的是請求的 URI
  3. “HTTP/1.1” 則代表的是協議以及協議版本

根據 HTTP 的規定,實際上請求方法有好幾種,就 HTTP1.1 而言它支持 8 種請求方法:OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT。在網頁中最常使用 GET 以及 POST 來傳遞封包。

GET 請求方法最常被用在請求資源,它會將所有 GET 的資訊利用 URI 全部寫在 HTTP header 中,直到寫滿為止。
因此若是可能會輸入個人隱私訊息的表單結果不應該使用 GET 請求方法,一旦被網路蜘蛛(爬蟲) 爬到該封包,個資便馬上外洩。

POST 請求方法最常被用在提交比較隱私的資訊,使用該請求方法時會將表單/輸入項的結果寫入 HTTP body 中。由於 HTTP 封包傳送的過程中會將 body 加密傳輸,故使用 POST 請求方法提交結果會比較安全。

HEAD 請求方法與 GET 相似,同樣會將資訊直接寫在 HTTP header 上,但是差別在於使用 HEAD 的請求方法時 server 端回應時不需要撰寫 HTTP body ,可以直接回傳 HTTP header 即可。
多用在測試 server 端服務效能。

PUT 請求方法多用在客戶端要更新資源,透過指定 server 底下的目錄為更新資源的目標路徑,將 HTTP body 中的資源寫到目標路徑中。
若是目標路徑不存在任何吻合的資源,那麼該次的請求便無效

PATCH 請求方法與 PUT 相似,多用在客戶端要更新資訊,只是與 PUT 差別在於:

  1. PATCH 用於資訊的一部分更新,而 PUT 則是用於全體的更新
  2. 當目標路徑的資源不存在的時候,PATCH 會自動新增,而 PUT 則只能對已存在的資源做更新

DELETE 請求方法多用在刪除 server 端上的目標路徑底下的資源。

不同的 Method 在當初 HTTP 的規定中主要是用來間接的告訴使用者應該怎麼樣操作這些網頁上的元素,
而不同的操作實際上也會有以下兩個性質:

  1. Safe :是否安全,如果會修改資料就是不安全,間接說明是否可以快取(不重複發出請求)
  2. Idempotent:是否可以在不確定有沒有成功送出時重新發出請求。

我們可以整理成以下表格:

總結

以上敘述的這些只是基本的 HTTP 概念,因為 HTTP 實際上就只是一種協定 (protocol),所以不論是 client 的 request 亦或是 server 的 response 遵循 HTTP packet format 之下才能被大部分的 web application 辨認
=> 不過 server 端實際上不一定要按照 method 去實作對應的功能,也可以達到使用 POST method 來實作 DELETE 的效果。

在我們講完了基礎網路傳輸以及 HTTP/HTTPS 後終於要進入正題了,什麼是 RESTFul?

REST = REpresentational State Transfer

工程師在架設網站後端時常常溝通上會提到 RESTFul API,API(application interface)應該不需要多做解釋,便是後端與前端溝通時的”窗口”,透過 URI 指定要存取或操作的資源。然而就 REST 的英文單詞來看它本身指出

  1. 每一個 URI 代表種資源
  2. client 端與 server 端之間、傳遞資源的表現
  3. client 端透過 HTTP 動詞(get、post、put、delete…),對 server 端資源進行操作,實現表現層狀態轉換

對 REST 作歸納的話可以歸結下面兩個特性:

統一的界面

REST 具有統一的介面,界面中都具有三要素:名詞 Noun、動詞 Verb、表徵 Content Type

  • Noun 就是要操作的 Resource,像是 https://github.com
  • Verb 是一組有限的操作指令,包含 GETPOSTPUTDELETE
  • Content Type 是一組有限的內容格式,也就是 Representation,像是 HTML。

每個請求可以視作針對某一件事情進行操作,但是都需要根據請求的內容定義 GET/POST/PUT/PATCH 等 method 以區別每個每個操作的實質內容。
從上面講可能還是會有點抽象,簡單來講我們在設計 RESTFul API 時會傾向於針對單一資源設計出不同操作的 API 給前端使用,那麼區分不同 API 的依據就會是每個 API 的 method。而設計出來的 API 可能會如下:
ex: GET /api/v1/GET/user/1 獲取 userID 為 1 的使用者資訊
ex: POST /api/v1/POST/user/2 新增 userID 為 2 的使用者資訊
ex: PATCH /api/v1/PATCH/user/2 更新 userID 為 2 的使用者資訊

要完全滿足 RESTFul API 設計規範則代表每個資源的操作你只能透過定義不同的 method 執行對應的行為

而其中像是 GET method 屬於 safe action,代表這種操作因為不會改變到原本的資源因此 server 端是可以考慮將這次的結果 cache 起來;反之,若是像 POST/PATCH 等 method 屬於 idempotent action,代表這種操作會修改到 server 上的資源不適合 cache 起來,但是相同的操作不論進行幾遍都會得到同樣的資源狀態結果。

而稍微思考一下就會發現若是完全符合 RESTFul 規範的 API 使用時就會發現在處理多筆資源的情況下 ,因為每次 request 都只能對單一資源進行操作,因此只能多次發送 request 才能一次完成多筆資源的操作,效能上就會降低許多。

為了解決這個問題,有些人會提倡說將 URI 的 param 直接傳入可快速分割的字串表示多筆處理,有些人則會提倡利用 POST 在 body 中附上所有需要操作的資源 ID 操作等等,以上這些做法雖然沒有違反 RESTFul 的設計規範,但是也並沒有被 RESTFul API 中的 RFC5789 所提倡,所以目前 facebook 有提出一個 solution: GraphQL,這部份未來有空的時候會做一篇詳細的文章來介紹一下它。

無狀態

由於 REST 是 REpresentational State Transfer 的縮寫,這意味著每個請求都必須包含請求相關的資訊,無論作為 URI 的查詢字串、參數、正文還是標題的一部分,在 server 端完成它的處理後會將這次的操作結果、狀態還傳給 client 端,然而在 server 端傳送過此次的操作結果後 server 端不該保存請求的相關資訊。

server 端不該存在任何手段用於紀錄會話狀態。

也就是說,對於 server 而言不應該具有能力辨認出每次的 request 的關聯性,會將每個 request 視作為不同的 client ,而 client 會在每次的請求中提供操作資源必要的資訊,使得 server 端能夠處理資源操作。在這種設計規範下所設計出來的 API 因為不需要額外處理儲存會話狀態的關係,某種程度上能簡化應用程式的設計。

客戶-伺服分離

遵循 REST 設計的系統可以做到 server 端與 client 端個別獨立實作,也就是說不論 client 端的程式碼在怎麼改變都不會影響到 server 端的服務,反之亦然。這是因為前面所提到的 REST 具有統一的界面,只要兩者規定好彼此溝通的形式就可以達到這種效果。

然而在這個特性上還有一個重要的部份是:對話只能由 client 端單方面發起,server 端接收 request 處理後才在回傳結果。

以上大概就是這次大叔整理出來的部分,比較屬於自己個人的筆記整理出來,所以在邏輯上可能存在 bug 還請多多包涵

更多相關資料:

https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm

--

--