(2-3) URI 設計 (URI Design) 提到,
HTTP 其中一個設計目標是:
分離『 資源辨識 (resource identification) 』 與 『 請求語意 (request semantics) 』
HTTP 請求方法 (Request Method),
是整個請求語意最主要的來源。
請求的 目標 (target) 是 資源 (resource),
而 請求方法 (Method) 則指出:
Client 發出此請求的 目的 (purpose) 與 期望的結果。
目錄
請求方法 (Request Method)
若 資源 (名詞) 是顆蘋果,到底是要 吃它? 削它? 煮它?
必須給他一個 『動詞』,來指名對它的操作。
HTTP 最初被設計為 分散式物件系統 (distributed object systems) 的使用介面,
請求方法 被設想為將 語意 (semantics) 應用於 目標資源。
一個理想的做法:
將請求方法 做為 動詞 (Method as Verb)
操作抽象 (Manipulating Abstraction)
如 (2-2) 資源、表示、URI 所述:
URI 對映的是一個 資源,資源 可以是任何東西,
而一個資源 (resource),是對映 (mapping) 到實體集合的『概念』(而非特定檔案)。
問題是:
使用者如何存取、操作 或 傳輸 "概念"?
Ans:
將被操作的對象,定義為 目標資源 的 表示 (representation),而非資源本身。
HTTP 以 統一介面 (uniform interface),供 Web 元件之間 (e.g., Client、Server),
傳輸 (transfer) 與 操作 (manipulate) 表示,藉此達到與資源的互動。
是 表示 狀態 傳輸 (Representational State Transfer, REST),
於 統一介面 中,最重要的 介面約束:
以 表示 (representation) 來操作資源。
manipulation of resources through representations.
Client 被限制在 操作表示,而非 直接尋訪 資源的內部實作 (e.g., server-side 閘道、資料庫、腳本),
因此,權威機構 (authority) 可依所期望的 任何形式 實作資源,而不影響 Client,
詳見 (1-3) 統一介面 (uniform interface)。
標準化方法 (standardized method)
然而,不像分散式物件,
HTTP 不可能為每種 資源 都定義一種 方法 (動詞),
做為 基於網路系統 的 統一介面 (uniform interface),必須提供更好的可見性 與 重用性。
於是,HTTP 定義了 標準化方法 (standardized method),
意圖應用於 任何資源,都能具有 相同的語義 (儘管每個資源是否實作或允許):
GET 、 POST 、 PUT 、 DELETE 、
HEAD 、 CONNECT 、 OPTIONS 、 TRACE
所有一般用途 Server 必須支援 GET 與 HEAD 方法,
剩下的方法則為 可選的。
其意義分別為:
請求方法 | 安全性 | 冪等性 | 可快取性 | 描述 |
---|---|---|---|---|
GET | O | O | O | 傳輸 目標資源 目前的 表示 (representation)。 |
HEAD | O | O | O | 與 GET 方法相同,但 Server 只傳輸 狀態行 以及 表頭部分。 |
POST | X | X | O [註] | 對請求的 酬載 (payload),執行 特定 資源的 處理。 |
PUT | X | O | X | 以請求的 酬載 (payload),替換 目標資源 所有目前的 表示 (representations)。 |
DELETE | X | O | X | 移除 目標資源 所有目前的 表示 (representations)。 |
CONNECT | X | X | X | 建立 「藉由 目標資源 辨識的 Server」的 隧道 (tunnel)。 |
OPTIONS | O | O | X | 描述 目標資源 的 通訊選項。 |
TRACE | O | O | X | 沿著 目標資源 的 路徑 執行 訊息 loop-back 測試。 |
[註]:
HTTP/1.1 規範中 POST 為可快取的,然實作上鮮少支援。
可以看到,請求方法 是操作 表示 (representation),而非直接尋訪資源的內部實作。
另外,請求方法 是 區分大小寫 (case-sensitive) 的,
按照慣例,標準化方法皆以 全大寫 US-ASCII 字母定義。
請求訊息 (Request Message)
在逐一介紹 請求方法 (request method) 前,
先快速複習 請求訊息 (Request Message)。
如 (3-1) 訊息格式 (Message Format) 範例,
請求方法 (request method) 位於請求訊息的開頭,
欲使用其他 方法,只需更改 POST 之處:
POST /task?id=1 HTTP/1.1
Host: echo.paw.cloud
Content-Type: application/json; charset=utf-8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:51.0) Gecko/20100101 Firefox/51.0
Connection: close
Content-Length: 136
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
無意義的 酬載主體 (訊息主體)
值得注意的是,
範例中,訊息主體 雖有實際的資料 — — 酬載 (payload):
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
然而,對於許多 請求方法 (e.g., GET、HEAD、DELETE、CONNECT、TRACE):
酬載 (payload) 是 未定義的語意 (無意義的),
若硬要送出,「可能」使 Server 拒絕此請求。
事實上,排除 TRACE 方法 外,HTTP 並無 嚴格限制 「請求方法 不准攜帶 酬載」,
換句話說,只要 Server 支援,一樣能在 GET 訊息 附加酬載送出。
(儘管這是不建議的做法)
訊息主體 (Message Body) 在 請求訊息中 是否存在,
由 Content-Length 或 Transfer-Encoding 欄位作為信號;
而與 方法語意 無關,即使該方法 未定義 訊息主體的任何用途。
許多人誤將 請求方法 視為訊息主體是否存在酬載的依據。
方法屬性 (Method Properties)
標準化 (standardized) 與 延伸方法,
皆註冊於 網際網路號碼分配局 (IANA),
且必須定義 安全性 (Safe) 與 冪等性 (Idempotent)。
安全方法 (Safe Methods)
如果定義的方法語義本質上是 唯讀 (read-only) 的,則該請求方法被認為是 安全的:
對 目標資源 應用 安全方法,源伺服器 (origin server) 不會 有任何 狀態改變 (state change)。
標準化方法 (standardized method) 中:
GET、HEAD、OPTIONS、TRACE 為 安全方法。
合理使用 安全方法 (safe method) 不應對原始服務器造成任何危害、資產損失或異常,
然而,這僅指 方法語意 與 資源的關係,無法確保 不會有 潛在 危害的 行為 實作。
例如,許多 Server 在每次回應完成後,都會附加 請求資訊 至 日誌 (log),
不考慮方法,若日誌儲存已滿且致 Server 當機,這也被視為 安全的。
同理,在網絡上選擇廣告而發起的 安全請求 通常會對廣告帳戶的收費產生額外作用。
非安全方法 (Unsafe Methods)
表示 (representation),
旨在透過協議容易傳達的格式,與伴隨著 元資料 (metadata),
以反映出 資源「現在」「過去」或「預期」的 『狀態 (state)』。
若應用一請求方法,可能造成 狀態改變 (State Change),
即為 — 非安全方法 (e.g., 資源的新增、修改、刪除…)。
標準化方法 (standardized method) 中:
POST、PUT、DELETE、CONNECT 為 非安全方法。
區分 安全 與 非安全方法,是為了:
允許自動檢索程序 (爬蟲) 和 快取效能優化 (預先載入) 的執行,而不用擔心造成危害。
HTTP/1.1 並無規範 狀態改變時 的主通通知機制,
能透過如 觀察者 (Observer) 或 發佈/訂閱 (Publish/Subscribe) …等模式,
並以 Ajax 或 類 Comet 架構 (e.g., EventSource) 實作非同步應用。
淺在危害實作 (Potentially Harmful Implementation)
然而,許多應用 違背了其安全屬性,
例如,基於 Web 的內容編輯軟體,
通常在 URI 的 查詢參數 (query parameter) 中指出行為動作,並送出 GET 請求。
(e.g., https://example.com?page=3&action=delete)
這不僅是 不良地 (2-3) URI 設計 (URI 不應包含動詞),
如上所述,GET、HEAD、OPTIONS、TRACE 為 安全方法,
「刪除」的行為帶來了 狀態的改變 (state change) (即 副作用),違反了 GET 的 安全性。
要注意的是:
狀態的改變 (state change) 不是 狀態傳輸/轉移 (state transfer)...
狀態的改變 (state change) 不是 狀態傳輸/轉移 (state transfer)...
狀態的改變 (state change) 不是 狀態傳輸/轉移 (state transfer)...
詳見 (1-3) 統一介面 (Uniform Interface)。
違反方法的安全性,可能使一些自動化程序造成危害,
[爬蟲 (spider)、預先載入 (pre-fetching)、建構 搜尋索引 (search index), etc.]
曾看過有人如此設計 blog URI,經 Google 爬蟲後,一覺醒來文章都沒了 😂:
https://example.com?article=2&action=delete
資源擁有者 (e.g., Server) 有責任確保 請求方法 與其安全屬性定義一致。
此外,安全方法 讓 使用者代理 (user agent) 在處理潛在不受信任的內容時,
對自動使用的非安全方法 進行適當約束,
並讓 使用者 在請求之前,了解行為是否安全。
冪等方法 (Idempotent Methods)
如果對 Server 送出單個請求,與送出多個相同請求的預期 效果相同 (回應訊息不同),
則該請求方法被認為是 冪等/等冪 (idempotent) [aɪˋdɛmpətənt] 。
與 安全方法 (safe method) 相同,這僅指 方法語意 與 資源的關係。
例如,Server 可以單獨每個請求 記錄日誌 (log),保留修訂歷史記錄,
或為每個 冪等請求 實現其他副作用,
而非送出一萬筆冪等請求,卻只有一筆日誌記錄。
標準化方法 (standardized method) 中:
PUT、DELETE 與 安全方法 (GET、HEAD、OPTIONS、TRACE) 為 冪等方法。
區分 冪等 與 非冪等方法,是為了:
當 Client 接收 回應前,若發生通訊錯誤,
冪等方法 能被自動地重複送出,並確保效果相同。
可快取方法 (Cacheable Methods)
如果允許將 回應 (response) 保存,供未來 等效的請求 重複使用,
以 減少回應時間 與 網路頻寬的消耗,增進效能,
則該請求方法被認為是 可快取的。
詳見 (6-1) 快取 (Cache)。
標準化方法 (standardized method) 中:
GET、HEAD 和 POST 為 可快取方法。
然而絕大多數實作中,只支援 GET 與 HEAD。