天天看點

RESTful API系列 - RESTful API是什麼以及其設計要點

REST API是什麼?

REST中文意思是REpresentational(代表性的)State(狀态)Transfer(傳輸),比較拗口,這個概念是Roy fielding提出的一種應用在分布式系統的架構風格。

很多時候不小心寫錯了,我們會看到Restful API,碰到這樣寫法,嚴格說是錯誤的。

簡單了解,RESTful API就是盡量滿足以下六個設計原則來進行實作的接口,它們是:

資料跟界面解耦

無狀态

可緩存

統一接口/标準化接口

分層次系統風格

按需編碼(可選)

這麼多個原則,換個簡化的說法,即是,更清晰更标準化更易擴充的接口。

還是很抽象的感覺?

下面舉幾個例子一一闡述這些原則。

No.1 資料跟界面解耦合

這個很好了解,接口要朝着提供視圖需要的資料的方向來設計,定制。

比如我們在做查詢使用者接口資料/api/user/001的響應時,應該僅僅傳回一個使用者的json資料

{
  "name":"levin",
  "photo":"baidu.com/ll/png234u1o343.png"
}      

視圖代碼如下(以web頁面為例子,界面代碼通常是一些标簽包圍起來的代碼塊)

<div>
    <div>Name:<span>{{name}}</span></div>
    <div>Photo:<img src="{{photo}}"/></div>
</div>      

而不是設計一個接口直接生成(當然這個原則并不能阻止部分程式員直接生成帶資料的視圖,最好不要這樣做,讓資料和界面分開可以有更高複用性)

<div>
   <div>Name:<span>levin</div>
   <div>Photo:<img src="baidu.com/ll/png234u1o343.png"/></div>
</div>
      

No.2 無狀态

首先,無狀态就是請求這個接口的時候,處理該請求的服務在未擷取資料時,該服務是沒有地方存儲待擷取的資料的。

舉個例子,還是/api/user/001 這個接口,背後的僞代碼可以如下:

let dataService = new DistributedDataService()
 
@Get("/api/user/{id}")
function getUser(id){
  return dataService.queryUser(id)
}      

這個接口的實作調用了dataService去查詢使用者編号是001的資訊。

服務本身是沒有存儲任何狀态的,資料可以從資料庫或者緩存中擷取,這就是保證服務的無狀态。

什麼情況是有狀态呢?

舉個例子,僞代碼如下:

//local data cache    
let dataService = LocalDataService()
    
@Get("/api/user/{id}")
function getUser(id){
    return dataService.queryUser(id)
}
@Delete("/api/user/{id}")
function getUser(id){
    return dataService.deleteUser(id)
}      

這裡使用的dataService是LocalDataService(本地的資料服務),通常單程序運作是沒有任何問題的。因為無論如何操作,隻要LocalDataService支援線程安全(這個需要開新的一篇來講),它的狀态嚴格上說不會有問題。

但在分布式系統中,普遍做法是部署多個伺服器多執行個體,而且他們同時提供/api/user/001接口的服務。

這時候問題就來了,每個服務程序都擁有一個localDataService。

當調用來DELETE /api/user/001,隻有一個服務的localDataService狀态更改了(少了001使用者)。

其他執行個體的本地資料仍有001使用者,下一次查詢的結果會有兩種,一種結果是傳回使用者沒找到,另一個結果是傳回001使用者,這就跟我們預期的不一緻,導緻了業務錯誤。

No.3 可緩存

應用緩存是一種提高性能的手段。這裡官方也沒有特别說明,其實符合普遍對資源的緩存的了解。

針對某個接口,可以設定請求頭Cache-Control, 可以是no-cache 或者max-age= 60 (某個數值)去告訴接口請求方可以緩存該接口資料多久。

舉個例子,我們進入百度搜尋RFC 7234(http協定關于緩存的标準)可以看到“消息頭”裡面關于緩存的設定,百度把一些不經常變化的資源标記了很大值,告訴火狐浏覽器,下次不需要重新下載下傳該資源。

https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/static/protocol/https/soutu/js/tu_68114f1.js
RESTful API系列 - RESTful API是什麼以及其設計要點

重新整理頁面,我們可以拿上面緩存的url進行搜尋,這裡顯示已經緩存了。

RESTful API系列 - RESTful API是什麼以及其設計要點

No.4 統一接口/标準化接口

官網提了,接口定義需要滿足4個限制:資源标示、通過呈現方式來操作資源、資源自描述、以及使用超媒體作為應用狀态。

舉個例子,像設計 /api/user/ 這個接口,如何符合第四原則滿足統一标準化接口?

首先,/api/user 這個uri定位了使用者資源相關的資訊操作,然後可以用GET來查詢使用者資訊,DELETE來删除使用者資訊。

再說資源自描述,我們查詢 /api/user/1001, 獲得一下響應消息:

{
   "id":"1001",
   "name":"levin",
   "country": "China"
}       

像這樣的就是資源自描述,這個響應告訴了我們使用者資訊的屬性,結構。我們查詢其他人像小明,也是擷取類似的回報。

至于超媒體作為應用狀态,這個不需要多說,簡單了解為多種格式的響應消息作為狀态即可。

這個設計原則,更多限制目标系統的REST API更加容易操作,可見可讀性。

No.5 分層次系統風格

這裡就是架構的分層,每層内的元件不允許看到與其不直接互動的層,也就是說,非直接互動的層對目前層内的元件不可通路。

RESTful API系列 - RESTful API是什麼以及其設計要點

比如上圖,A <->B<->C,三層架構中,AB緊鄰,BC緊鄰,這樣分層避免了AC直接交流,A層的變動不會對C層造成影響。

這樣設計可以更好的讓層與層之間互相解耦,讓每一層專注做單層的功能(當然分層數過多也有壞處)。

No.6 按需編碼(可選)

伺服器可以提供一些代碼或者腳本并在客戶的運作環境中執行, 比如一些JS腳本供用戶端下載下傳調用。

像過去Java伺服器端可以生成Applet腳本供用戶端執行。

說到這裡跟按需編碼(CodeOnDemand), 好像表達的意思不太貼切,我們隻需要明白這個設計原則是為了提高用戶端的擴充性即可。

上面說那麼多,其實RESTful API本質是通過走HTTP協定來呈現接口。

通過uri為系統互動的接口,比直接代碼調用代碼更加簡化,而且脫離了語言限制。

更多細節參考:

https://restfulapi.net/ https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control https://www.rfc-editor.org/info/rfc7234 https://www.rfc-editor.org/rfc/inline-errata/rfc2616.html