被誤用的羅伊·菲爾丁的有關描述性狀態遷移(REST)的博士論文

硬核老王 發佈 2024-03-07T08:03:29.320337+00:00

符合 描述性狀態遷移REpresentational State Transfer(LCTT 譯註:REST,譯自審定公布名詞資料庫)的應用程式接口(API)無處不在。有趣的是又有多少人真正了解「符合描述性狀態遷移」的應有之義呢?

符合 描述性狀態遷移REpresentational State Transfer(LCTT 譯註:REST,譯自審定公布名詞資料庫)的應用程式接口(API)無處不在。有趣的是又有多少人真正了解「符合描述性狀態遷移」的應有之義呢?

大概我們中的大多數人都會跟 黑客新聞網站上的這篇公開問答產生共鳴:

我閱讀了幾篇介紹描述性狀態遷移(REST)的文章,甚至包括原始論文的部分章節。然而我仍然對REST 到底是什麼只有一個相當模糊的想法。我開始認為沒有人真的了解它,它僅僅是一個定義相當不充分的概念。

我曾經計劃寫一篇有關 REST 的博客,在裡面探討 REST 是如何成為這樣一個在網絡通信領域占主導地位的範式的。我通過閱讀 2000 年發表的 羅伊·菲爾丁Roy Fielding 的博士論文開始我的研究,這篇博士論文向世人介紹了 REST 的概念。在讀過菲爾丁的博士論文以後,我意識到,相比之下,更引人注意的是菲爾丁的理論緣何受到如此普遍的誤解。

(相對公平地說,)很多人知道 REST 源自菲爾丁的博士論文,但並沒有讀過該論文。因此對於這篇博士論文的原始內容的誤解才得以流行。

最大的誤解是:這篇博士論文直接解決了構建 API(API)的問題,我此前一直認為 REST 從一開始就打算成為構建在超文本傳輸協議(HTTP)之上的 網絡 APIWeb API的架構模型,我相信很多人也這樣認為。我猜測此前可能存在一個混亂的試驗時期,開發人員採用完全錯誤的方式在 HTTP 基礎上開發 API,然後菲爾丁出現了,並提出了將 REST 做為網絡應用程式開發的正確方式。但是這種想法的時間線對不上:我們今天所熟知的網絡服務的 API 並非是在菲爾丁出版他的博士論文之後才出現的新生事物。

菲爾丁的博士論文《架構風格與基於網絡的軟體架構設計》不是討論如何在 HTTP 的基礎上構建 API,而恰恰是討論 HTTP 本身。菲爾丁是 HTTP/1.0 版規範的貢獻者,同時也是 HTTP/1.1 版的共同作者。有感於從 HTTP 的設計中獲得的架構經驗,他的博士論文將 REST 視為指導 HTTP/1.1 的標準化過程的架構原則的精華。舉例而言,他拒絕了使用新的 MGETMHEAD方法進行批量請求的提議,因為他認為這違反了 REST 所定義的約束條件,尤其是在一個符合 REST 的系統中傳遞的消息應該是易於代理和緩存的約束條件。[1]因此,HTTP/1.1 轉而圍繞持久性連接設計,在此基礎上可以發送多個 HTTP 請求。(菲爾丁同時認為網頁保存在本地的瀏覽數據,即 cookie 是不符合 REST 的,因為它們在一個狀態無關的系統中增加了狀態描述,然而它們的應用已經根深蒂固了。[2])菲爾丁認為,REST 並非構建基於 HTTP 的系統的操作指南,而是擴展 HTTP 的操作指南。

這並不意味著菲爾丁認為 REST 不能用於構建其他系統。只是他假定其他系統也是 「分布式超媒體系統distributed hypermedia systems」。人們對 REST 還有另一個誤解:認為它是一個可以用在任何網絡應用中的通用架構。但是從這篇博士論文中菲爾丁介紹 REST 的部分你基本上能夠得出如下的結論,「請注意,我們只設計了 HTTP,但是如果你發現你自己也在設計一個_分布式超媒體系統_,你也應該採用我們提出的稱為 REST 的優秀架構,以讓開發變得更容易」。有鑑於網際網路已經存在了,我們尚不清楚為什麼菲爾丁認為有人可能試圖重新創建這樣一個(和 HTTP 一樣的)系統。或許在 2000 年的時候世界上仍然存在不只一個分布式超文本系統的空間吧。無論如何,菲爾丁已經說得很清楚了,REST 意在提供一套解決方案,來解決在試圖經由網絡連接超文本內容時出現的可擴展性與一致性問題,而不是作為分布式應用的通用架構模型。

我們現在只記得菲爾丁的博士論文提出了 REST 的概念,但事實上,他的博士論文是在討論一刀切的軟體架構有多麼糟糕,以及如何更好地選擇符合你需求的軟體架構。這篇博士論文中僅用了一個章節來討論 REST 本身,大量的文本內容都都花在了對你能夠在網絡應用中運用的不同的架構風格 [3]的分類上。這些架構風格包括:受 Unix 的管道設計啟發的 管道-過濾器Pipe-and-Filter (PF)風格,客戶-伺服器Client-Server (CS)風格的多種改進,如 分層-客戶-伺服器Layered-Client-Server(LCS)風格、客戶-緩存-無狀態-伺服器Client-Cache-Stateless-Server(C$SS)風格和分層-客戶-緩存-無狀態-伺服器Layered-Client-Cache-Stateless-Server(LC$SS)。這些縮略詞未必實用,但是菲爾丁認為我們可以通過混合匹配現有風格提供的約束條件來派生出新的風格。REST 就是通過這種方式產生的,它本可以稱之為 一致性-分層-按需代碼-客戶-緩存-無狀態-伺服器Uniform-Layered-Code-on-Demand-Client-Cache-Stateless-Server(ULCODC$SS)風格,顯然我們並沒有這樣做。菲爾丁建立上述分類是為了強調(每一種風格對應的)約束適用於不同的應用,而上述最後一種風格對應的約束是他所認為的最適用於 HTTP 的。

今天,REST 的無處不在是極具諷刺意味的。REST 在各種各樣的網絡應用中被盲目使用,但是菲爾丁最初只是將 REST 視作一種指引,通過它指導人們裁剪一種軟體架構來適應獨立應用的特殊需求。

我很難弄清楚這是如何發生的,畢竟菲爾丁已經明確地指出了不能讓形式服從功能的陷阱。他在論文的一開始就作出了警告:由於沒有正確地理解軟體架構而「使用流行的架構設計是經常發生的」[4]。他在幾頁之後又重新強調了這一點:

一些架構風格經常被描述為適用於一切軟體形式的「銀彈」解決方案。然而,一名好的設計者應該選擇能夠滿足解決特定問題的需要的架構風格[5]。

REST 本身就是一個特別糟糕的 「銀彈」 解決方案。正如菲爾丁所指出的,它包含了可能不合適的利弊權衡,除非你試圖構建一個分布式超媒體應用:

REST 設計用來高效地進行大粒度的超媒體數據傳輸,並對網絡應用場景中的常用見情形做了優化,但是可能會導致其在與其他形式的軟體架構相互作用時不協調。[6]

菲爾丁提出 REST 是因為網絡發展帶來了「超越政府的可擴展性anarchic scalability」這一棘手問題,菲爾丁的意思是需要以一種高性能的方式跨越組織和國家邊界連接文件。REST 所施加的約束是經過精心選擇的,以用來解決這一「超越政府的擴展性」問題。面向公眾的網絡服務 API 同樣需要解決類似的問題,因此可以理解為什麼 REST 在這些應用中是適用的。然而,時至今日,如果發現一個工程團隊使用 REST 構建了一個後端,即使這個後端只與工程團隊完全控制的客戶端通訊,也不會令人驚訝。我們都成為了蒙蒂巨蟒Monty Python 的獨幕滑稽劇中的建築師,那位建築師按照屠宰場的風格設計了一座公寓大樓,因為屠宰場是他唯一具有的經驗的建築。(菲爾丁使用了這部滑稽劇中的一句台詞作為他的博士論文的題詞:打擾一下,你說的是「刀」嗎?)(LCTT 校註:順便說一句,Python 語言的名稱來自於 「Monty Python」 這個英國超現實幽默表演團體的名字。)

有鑑於菲爾丁的博士論文一直在極力避免提供一种放之四海而皆準的軟體架構,REST 又怎麼會成為所有網絡服務的事實上的標準呢?

我認為,在 21 世紀頭十年的中期人們已經厭倦了簡單對象訪問協議(SOAP),因此想要創造另一種屬於他們自己的四字首字母縮略詞。

我只是半開玩笑。簡單對象訪問協議Simple Object Access Protocol(SOAP)是一個冗長而複雜的協議,以致於你沒法在不事先理解一堆互相關聯的可擴展標記語言(XML)規範的基礎上使用它。早期的網絡服務提供基於 SOAP 的 API。在 21 世紀頭十年中期,隨著越來越多的 API 開始提供,被 SOAP 的複雜性激怒的軟體開發者隨之集體遷移。

SOAP 遭到了這群人的蔑視,Rails 之父 戴維·海涅邁爾·漢森David Heinemeier Hansson(LCTT 譯註:譯自其所著的《重來》的中文版的作者譯名)曾經評論:「我們感覺 SOAP 過於複雜了,它已經被企業人員接管。而當這一切發生的時候,通常沒有什麼好結果。」[7]始於這一標誌性的評論,Ruby-on-Rails 於 2007 年放棄了對 SOAP 的支持。「企業人員」總是希望所有內容都被正式指定,反對者認為這是浪費時間。

如果反對者不再繼續使用 SOAP,他們仍然需要一些標準化的方式來進行工作。由於所有人都在使用 HTTP,而且代理與緩存的所有支持,每個人都至少會繼續使用 HTTP 作為傳輸層,因此最簡單的解決方案就是依賴 HTTP 的現有語義。這正是他們所做的工作。他們曾經稱之為:去它的,重載 HTTPFuck It, Overload HTTP(FIOH)。這會是一個準確的名稱,任何曾經試圖決定業務邏輯錯誤需要返回什麼 HTTP 狀態碼的人都能證明這一點。但是在所有的 SOAP 正式規範工作的映襯下,這顯得魯莽而乏味。

幸運的是,出現了這篇由 HTTP/1.1 規範的共同作者創作的博士論文。這篇博士論文與擴展 HTTP 有某種模糊的聯繫,並給予了 FIOH 一個具有學術體面的外表。因此 REST 非常適合用來掩飾其實僅僅是 FIOH 的東西。

我並不是說事情就是這樣發生的,也不是說在不敬業的創業者中確實存在著盜用 REST 的陰謀,但是這個故事有助於我理解,在菲爾丁的博士論文根本就不是討論網絡服務 API 的情況下,REST 是如何成為用於網絡服務 API 的架構模型的。採用 REST 的約束存在一些效果,尤其是對於那些面向公眾的需要跨越組織邊界的 API 來說。這些 API 通常會從 REST 的「統一接口」中受益。這應該是 REST 起初在構建網絡 API 時被提及的核心原因。但是,想像一下一種叫做 「FIOH」 的獨立方法,它借用 「REST」 的名字只是為了營銷,這有助於我解釋我們今天所知道的 REST 式RESTful API 與菲爾丁最初描述的 REST 的架構風格之間的諸多差異。

舉例而言,REST 純粹主義者經常抱怨,那些所謂 RESTful API 實際並不是 REST API,因為它們根本就沒有使用 超文本作為應用程式狀態引擎Hypermedia as The Engine of Application State(HATEOAS)。菲爾丁本人也做出過 這樣的批評。根據菲爾丁的觀點,一個真正的 REST API 應當允許你通過跟隨連結實現從一個基礎端點訪問所有的端點。如果你認為這些人的確在試圖構建 RESTful API,那麼存在一個明顯的疏漏 —— 使用超文本作為應用程式狀態引擎(HATEOAS)的確是菲爾丁最初提出的 REST 概念的基礎,尤其是考慮到「描述性狀態遷移(REST)」中的「狀態遷移(ST)」意指使用資源之間的超連結進行狀態機的導航(而不是像很多人所相信的那樣通過線路傳輸資源狀態)[8]。但是你試想一下,如果每個人都只是在構建 FIOH 的 API,並明里暗裡的將之作為 REST API 宣傳,或者更誠實一點說是作為 「RESTful」 API 宣傳,那麼自然使用超文本作為應用程式狀態引擎(HATEOAS)也就不重要了。

類似的,你可能會感到驚訝:儘管軟體開發者喜歡不斷地爭論使用 PUT方法還是使用PATCH方法來更新資源更加 RESTful,菲爾丁的博士論文卻沒有討論哪個 HTTP 的操作方法應該映射到增刪改查(CURD)操作。在 HTTP 操作與 CURD 操作之間建立一個標準的映射表是有用的,但是這一映射表是 FIOH 的一部分而不是 REST 的一部分。

這就是為什麼,與其說沒有人理解 REST,不如說我們應該認為 「REST」 這一術語是被誤用了。REST API 這一現代概念與菲爾丁的 REST 架構之間存在歷史聯繫,但事實上它們是兩個不同的概念。歷史聯繫適合作為確定何時構建 RESTful API 的指引而留在心底。你的 API 需要像 HTTP 那樣跨越組織和政府邊界嗎?如果是的話,那麼構建具有統一的可預測的接口的 RESTful API 可能是正確的方式。如果不是的話,你最好記住,菲爾丁更傾向於形式服從功能。或許類似 GraphQL 的方案或者僅僅 JSON-RPC 更適合你試圖完成的工作。

  1. Roy Fielding. 「Architectural Styles and the Design of Network-based Software Architectures,」 128. 2000. University of California, Irvine, PhD Dissertation, accessed June 28, 2020, https://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation_2up.pdf.↩︎

  2. Fielding, 130. ↩︎

  3. Fielding distinguishes between software architectures and software architecture 「styles.」 REST is an architectural style that has an instantiation in the architecture of HTTP. ↩︎

  4. Fielding, 2. ↩︎

  5. Fielding, 15. ↩︎

  6. Fielding, 82 ↩︎

  7. Paul Krill. 「Ruby on Rails 2.0 released for Web Apps,」 InfoWorld. Dec 7, 2007, accessed June 28, 2020, https://www.infoworld.com/article/2648925/ruby-on-rails-2-0-released-for-web-apps.html↩︎

  8. Fielding, 109. ↩︎

via: https://twobithistory.org/2020/06/28/rest.html

作者:Two-Bit History選題:lujun9972譯者:CanYellow校對:wxy

本文由 LCTT原創編譯,Linux中國榮譽推出

關鍵字: