RESTful 介面實現簡明指南

2021-10-01 12:41:23 字數 4783 閱讀 1248

在前後端分離的 web 應用架構中,前端專注於頁面,同時與後端進行資料互動;而後端則專注於提供 api 介面。在這樣的結構下,rest 是乙個很流行的前後端互動形式的約定。這只是一套約定,並不是某個技術標準,所以在實際的應用中,對器實現程度完全取決於後端開發者;一些號稱 restful 的介面並沒有那麼restful。

在我所見過的 restful 介面的實現中,以 github 最讓人驚嘆。我第一次如此強烈得感受到 rest 介面的美妙,完全滿足了我所期待的「介面的形式美感」,簡直就是對 rest 規範實現的最佳範本。我覺得每乙個後端開發者都應該看一看 github 的 rest 介面文件,感受一下循規蹈矩的美妙。

本文選取了幾個點來簡要介紹,乙個讓前端開發者用起來舒服的 restful 介面是什麼樣子。

以防有些觀眾剛剛開啟電梯,還是有必要簡單介紹一下 rest 這個概念。

rest 是乙個術語的縮寫,representational state transfer,中文直譯「表徵狀態轉移」,這是個很拗口的詞。我的建議是先不要強行理解,直接看怎麼做,等對實施細節有一些了解後,再來看名字會有更深刻的理解。rest 是一套風格約定,restful 是它的形容詞形式;比如一套實現了 rest 風格的介面,可以稱之為 restful 介面。

rest 用來規範應用如何在 http 層與 api 提供方進行資料互動;在現階段,你應該已經很熟悉 get 和 post 請求;甚至有可能因為受限於後端框架限制等原因,你的整個應用全都是用這兩種 http 方法來完成的。這樣無疑浪費了 http 協議的潛力,而 rest 則充分利用了 http 規範中的方法,達到介面描述的語義化。

rest 描述了 http 層裡客戶端和伺服器端的資料互動規則;客戶端通過向伺服器端傳送 http(s)請求,接收伺服器的響應,完成一次 http 互動。這個互動過程中,rest 架構約定兩個重要方面就是 http 請求的所採用方法,以及請求的鏈結。

在請求層面,rest 規範可以簡單粗暴抽象成以下兩個規則:

1. 請求 api 的 url 表示用來定位資源;

2. 請求的 method 表示對這個資源進行的操作;

以下將以這兩個規則為基礎,描述如何構造乙個符合 rest 規範的請求。

一、api 的 url

url 用來定位資源,跟要進行的操作區分開,這就意味這 url 不該有任何動詞;

下面示例中的 get、create、search 等動詞,都不應該出現在 rest 架構的後端介面路徑中。在以前,這些介面中的動名詞通常對應後台的某個函式。比如:

/api/getuser

/api/searchresult

/api/deleteallusers

當我們需要對單個使用者進行操作時,根據操作的方式不同可能需要下面的這些介面:

/api/getuser (用來獲取某個使用者的資訊,還需要以引數方式傳入使用者 id 資訊)

/api/updateuser (用來更新使用者資訊)

/api/deleteuser (用來刪除單個使用者)

/api/resetuser (重置使用者的資訊)

更有甚者,可能在更新使用者不同資訊時,提供不同的介面,比如:

/api/updateusername

/api/updateuseremail

/api/updateuser

這樣的弊端在於:首先加上了動詞,肯定是使 url 更長了;其次對乙個資源實體進行不同的操作就是乙個不同的 url,造成 url 過多難以管理。

其實當你回過頭看「url」 這個術語的定義時,更能理解這一點。url 的意思是統一資源定位符,這個術語已經清晰的表明,乙個 url 應該用來定位資源,而不應該摻入對操作行為的描述。

url 中不應該出現任何表示操作的動詞,鏈結只用於對應資源;

url 中應該單複數區分,推薦的實踐是永遠只用複數;比如 get /api/users 表示獲取使用者的列表;如果獲取單個資源,傳入 id,比如 /api/users/123 表示獲取單個使用者的資訊;

按照資源的邏輯層級,對 url 進行巢狀,比如乙個使用者屬於某個團隊,而這個團隊也是眾多團隊之一;那麼獲取這個使用者的介面可能是這樣:

get /api/teams/123/members/234 表示獲取 id 為 123 的小組下,id 為234 的成員資訊
按照類似的規則,可以寫出如下的介面

/api/teams (對應團隊列表)

/api/teams/123 (對應 id 為 123 的團隊)

/api/teams/123/members (對應 id 為 123 的團隊下的成員列表)

/api/teams/123/members/456 (對應 id 為 123 的團隊下 id 未 456 的成員)

二、api 請求的方法

在很多系統中,幾乎只用 get 和 post 方法來完成了所有的介面操作;這個行為類似於全用 div 來布局。實際上,我們不只有get 和 post 可用,在 rest 架構中,有以下幾個重要的請求方法:get,post,put,patch,delete。這幾個方法都可以與對資料的 crud 操作對應起來。

【read】,資源的讀取,用 get 請求;比如:

get /api/users  ( 表示讀取使用者列表)
get 應當實現為乙個安全方法。用於獲取資料而不應該產生***。

【created】,資源的建立,用 post 方法;

post 是乙個冪等的方法,多次呼叫會造成不同效果;

冪等(idempotent):如果對伺服器資源的多次請求與一次請求造成的***是一樣的的話,那這個請求方法可以被認為是冪等。
比如下面的請求會在伺服器上建立乙個 name 屬性為 'john snow' 的使用者;多次請求就會建立多個這樣的使用者。

post /api/users

【update】,資源的更新。用於更新的 http 方法有兩個,put 和 patch。

他們都應當被實現為冪等方法,即多次同樣的更新請求應當對伺服器產生同樣的***。

put 和 patch 有各自不同的使用場景:

put 用於更新資源的全部資訊,在請求的 body 中需要傳入修改後的全部資源主體;

而 patch 用於區域性更新,在 body 中只需要傳入需要改動的資源字段。

設想伺服器中有以下使用者資源 /api/users/123

當我們往後臺傳送更新請求時,patch 和 put 造成的效果是不一樣。

put /api/users/123

上述 put 請求操作後的內容是:

可以觀察到,資源原有的 age 欄位被清除掉了。

而如果改用 patch 的話,

patch /api/users/123

更新後的內容是:

請求中指定的 name 屬性被更新了,而原有的 age 屬性則保持不變。

patch 的作用在於如果乙個資源有很多字段,在進行區域性更新時,只需要傳入需要修改的字段即可。否則在用 put 的情況下,你不得不將整個資源模型全都傳送回伺服器,造成網路資源的極大浪費。

【delete】,資源的刪除,相應的請求 http 方法就是 delete。這個也應當被實現為乙個冪等的方法。如:

delete /api/users/123
用於刪除伺服器上 id 為 123 的資源,多次請求產生***都是,是伺服器上 id 為 123 的資源不存在。

三、分頁、過濾

rest 風格的介面位址,表示的可能是單個資源,也可能是資源的集合;當我們需要訪問資源集合時,設計良好的介面應當接受引數,允許只返回滿足某些特定條件的資源列表。

比如支援以 offset 和 limit 引數來進行分頁;

get /api/users?offset=0&limit=20
get /api/users?keyword=john&sort=age
支援根據字段進行過濾

get /api/users?gender=male
設計合適的 api url,以及選擇合適的請求方法,可以語義化的描述乙個 http 請求的操作。

當我們都熟悉且遵循這樣的規範後,基本可以看到乙個 rest 風格的介面就知道如何使用這個介面進行 crud 操作了。比如下面這面這個介面就表示搜尋 id 為 123 的圖書館的書,並且書的資訊裡包含關鍵字「game」,返回前十條滿足條件的結果。

get /api/libraries/123/books?keyword=game&sort=price&limit=10&offset=0
同樣,下面這個請求的意思也就很明顯了吧。

patch /api/companies/123/employees/234

總結

限於篇幅限制,本文僅從請求的角度選取了幾個重點描述了實現 restful 介面需要滿足的基本要求。關於 rest 的更多詳細規範,可以參考這個倉庫。

RESTful 介面實現簡明指南

rest 是乙個術語的縮寫,representational state transfer,中文直譯 表徵狀態轉移 這是個很拗口的詞。我的建議是先不要強行理解,直接看怎麼做,等對實施細節有一些了解後,再來看名字會有更深刻的理解。rest 是一套風格約定,restful 是它的形容詞形式 比如一套實現...

git 簡明指南

助你入門 git 的簡明指南,木有高深內容 建立新資料夾,開啟,然後執行 git init 以建立新的 git 倉庫。執行如下命令以建立乙個本地倉庫的轉殖版本 git clone path to repository 如果是遠端伺服器上的倉庫,你的命令會是這個樣子 git clone usernam...

git 簡明指南

建立新倉庫 建立新資料夾,開啟,然後執行 git init 以建立新的 git 倉庫。檢出倉庫 執行如下命令以建立乙個本地倉庫的轉殖版本 git clone path to repository 如果是遠端伺服器上的倉庫,你的命令會是這個樣子 git clone username host path...