1. 程式人生 > 程式設計 >RESTful 架構

RESTful 架構

author githubId: golone

前言

看了兩天 RESTful 總結的筆記,第一次在掘金髮文章,若有謬誤還請大佬們海涵。

概念

  1. RPC 面向方法
  2. SOA 面向訊息
  3. REST 面向資源

REST:

REST(Representational State Transfer)是種網際網路軟體架構模式,由 Roy Fielding 在他 2000 年的博士論文《架構風格與基於網路的軟體架構設計》(譯)中提出,REST 一經提出就流行起來, 迅速取代了複雜笨重的 SOAP。

要理解 REST,最好是認真理解 Representational State Transfer 是什麼,它的直譯是“表現層狀態轉化”,其中省略了主語,完整的意思應該是“資源的表現層狀態轉化”,關鍵詞是“資源”、“表現層”、“狀態轉化”。

資源:

“資源”就是網路上的一個資訊實體,REST 規定用一個 URI(統一資源定位符)來唯一標識一個資源,通過 URI 可以獲取到任意資源。

表現層:

“資源”只是資訊,它可以有多種存在方式(比如一段單純的文字可以被表示為 txt、HTML、XML、JSON,甚至二進位制等多種格式),我們把資源所處的存在方式稱為它的“表現層”, 可以把“表現層”理解為資源的“檢視”,在 REST 裡,URI 只用來標識資源,並不標識它處於哪個表現層,它的表現層是在 HTTP header 中由 Accept、Content-Type 指定的,這兩個欄位用於標識資源當前所處 的“表現層”。

狀態轉化:

“狀態”包括資源本身資訊和它所處的表現層兩個方面(這個概念可能是我造的,我查的資料裡並沒有把“狀態”作為一個單獨的概念過,但我覺得應該加這麼個概念,不然邏輯上有些不通), “狀態轉化”是指資源從一種“狀態”改變到另一種“狀態”

,這個“改變”可能是表現層的改變,也可能是資源本身資訊的改變,訪問介面,就是通知服務端進行“狀態轉化”, 比如 CURD:

操作 影響
資訊改變,多了一些資訊。
資訊改變,少了一些資訊。
資訊改變,變了一些資訊。
表現層改變,資料被從資料庫中取出轉為 html、json 等。

REST 規定用 HTTP method 來作為告知服務端進行“狀態轉化”的手段,具體的說就是用 HTTP 的不同請求方法來指定不同的操作,比如常見的 GET、POST、PUT、DELETE 分別對應 CURD, 告知服務端進行增刪改查四種狀態轉化。

以上是 REST 的核心概念,嚴格來說它有以下幾條約束:

  1. 每個資源都擁有一個唯一的資源標識。
  2. 訊息的自描述性。
  3. 資源的自描述性。

RESTful:

如果一個架構符合 REST 的規範,就稱它為 RESTful 架構,採用 RESTful 架構的 web 服務也被稱為 Restful web service。

在 Restful web service 中,每個 url 代表一種資源(resource),所以 url 中不能有動詞,只能有名詞,甚至所用的名詞往往與資料表名相對應,而資料表通常包含不止一條記錄, 所以 url 中的名詞通常用複數。

補充:

這些是我參考的資料中,我覺得有趣或專業的幾篇:

  1. 一篇很有趣的講 RESTful api 的文章:如何給老婆解釋什麼是 RESTful
  2. REST 的侷限性
  3. 如果你想知道 REST 是怎麼來的:推導 REST

相比 REST,api 設計在 2015 年又出了套更有革命意義的理論 GraphQL,基本思路是在前、後端間放一箇中間伺服器,然後定義一種查詢語言讓前端描述所需資料,中間伺服器接收前端發來的查詢進行解析, 然後自動請求對應的介面並整理資料後再返回給前端,好處是前端獲取資料時不用請求多個介面,一次搞定,也不用記介面路由、引數,這些東西前端都不用管,只管查就是了, 說起來就是個類似 sql 的東西,很方便,詳情可以看這兩篇文章:REST 將會過時, 而 GraphQL 則會長存REST 2.0 在此,它的名字叫 GraphQL

URL 設計

RPC 以動詞為中心,RESTful 以名詞為中心。

加入新功能時,以動詞為中心往往必須新增新的動詞,並且後端要實現該動詞,前端要知道這個新的動詞並進行呼叫; 而以名詞為中心則沒這麼麻煩,無論要加什麼新功能, 只要沒有新增新的資源,url 是不變的,前後端只需關注各自實現即可,不需要在介面上分心。以名詞為中心設計 RESTful 風格的介面時,應注意以下幾點:

  1. 單、複數,這很好理解,根據資源的數量確定用哪種形式的名詞,不贅述了。
  2. 資源等級,資源分主資源、子資源,主資源代表一類資源,所以主資源應放在 url 中間而不是末尾,例如 /users/friends,users 代表所有使用者,friends 代表該使用者的所有好友。
  3. query、params,RESTful 比較提倡 params 方式傳參,例如將 /v1/users?id=x 寫成 /v1/users/:id,引數放在路徑中,全部這樣做顯然不現實,比如檔案上傳、 存在多個可選引數等,但除這些之外,應儘量用 params 方式傳參。
  4. HTTP 方法,RESTful 不允許在 url 中使用動詞,而 HTTP1.1 剛好定義了 8 個方法指代 8 個動詞,所以 RESTful 提倡用 http 提供的方法來描述常見操作,例如 用 put、post、get、delete 來分別對應資源的 CURD。

引數:

既然 url 代表資源,那麼進一步,可不可以讓 url 更接近 sql?這樣可以使介面變得更靈活,基於這點,RESTful api 有了以下這些常見引數:

欄位 作用
limit 指定返回記錄的數量。
offset 指定返回記錄的開始位置。
page_number、page_size 指定第幾頁,以及每頁的記錄數。
orderBy、order 指定返回結果按照哪個屬性排序,以及排序順序。
where 直接指定 where 條件。
id 指定記錄 id。

PS:有些 client 只支援 GET、POST,因此必須由服務端來模擬其它方法,這可以通過給請求的 header 加上 X-HTTP-Method-Override 欄位來實現,它會告訴服務端應該用哪個方法,例如:

        POST /api/Person/4 HTTP/1.1
        X-HTTP-Method-Override: PUT

這樣,X-HTTP-Method-Override 就指定該次請求的方法是 PUT,而不是 POST。

響應報文

狀態碼:

為了保證訊息的自描述性,應該使用以下狀態碼來表示常見情況:

code 含義
200(OK) 請求成功,沒毛病。
204(無內容) 請求成功,但資源為空。
301(Moved Permanently) 當前資源 URI 已變更。
400(bad request) 無效請求(如引數錯誤)
404(not found) 資源不存在。
500(internal server error) 伺服器內部錯誤。
503(Service Unavailable) 服務端暫無法處理請求。

不要返回純本文:

介面返回的資料應是 JSON 格式,相應的 response header 的 Content-Type 要設為 application/json。前端請求時,也應明確告訴伺服器接受 JSON 格式,即 request header 的 ACCEPT 要設為 application/json。JSON 格式並不是 RESTful 要求的,只是當下流行的資料傳輸格式,類似不成文規定,以後可能會變。(2019-11-23

提供連結:

API 的使用者未必知道 URL 是如何設計的,因此 RESTful api 還要求資源自描述性。阮一峰部落格裡記載的一個解決方法是,在響應資料中給出相關連結。 這樣使用者只要記住一個 URL,就可以發現其他 URL。這種方法叫做 HATEOAS。比如 GitHub 的 API 都記載在 api.github.com ,訪問它,就可以得到所有 URL, 或者訪問其中一個 URL,返回與其相關的 URL。這樣,使用者不需要記住 URL 設計,只要從 api.github.com 一步步查就可以了。

個人看法

RESTful api 可以很好的實現一套介面,多端使用,非常優秀,但完全按照它的標準來做我覺得不行,譬如 url 中不能包含動詞這點,動詞單純由 http 方法來提供未免不夠用,有時 api 的作用並不只是對 資源進行 CURD 四個操作,這就需要更多地動詞,所以我覺得必要時在 url 內加個動詞是很正常的。(而且我還是喜歡只用 get、post,不想搞那麼多麼蛾子方法,加個動詞又不會死 。。。

另外是引數問題,我看過的技術部落格在列舉 RESTful api 時普遍有類似 /users/123/articles/54 的例子,我覺得改為 /users/articles/123/54 才是 更合適的做法,這樣後端在定義路由時就可以寫為 /users/articles/:userId/:articleId,先路徑,再引數,集中存放,比較舒服。