动态

详情 返回 返回

RESTful API 設計方法:打造高質量、易用的接口系統 - 动态 详情

RESTful 架構基礎

REST,代表表現層狀態轉移(Representational State Transfer),長久以來一直是 API 服務的聖盃,最初由 Roy Fielding 在其博士論文中定義。儘管它不是構建 API 的唯一方法,但由於其廣泛的普及,即使是非開發者也對其有所瞭解。

RESTful 軟件具有六個關鍵特徵:

  • 客户端-服務器架構
  • 無狀態性
  • 可緩存性
  • 分層系統
  • 按需代碼(可選)
  • 統一接口

但這些還太理論化了,我們需要一些更具操作性的內容,那就是 API 成熟度模型。

Richardson 成熟度模型

由 Leonard Richardson 開發,該模型將 RESTful 開發的原則合併為四個易於遵循的步驟。

等級 0:POX 的沼澤

一個 0 級 API 是一組簡單的 XML 或 JSON 描述。在介紹中,我提到在 Fielding 的論文之前,RESTful 原則被稱為 “HTTP 對象模型”。

這是因為 HTTP 協議是 RESTful 開發的最重要部分。REST 圍繞儘可能多地使用 HTTP 的固有屬性的理念展開。

在 0 級,你根本不使用這些東西。你只是構建自己的協議並將其用作專有層。這種架構被稱為遠程過程調用(RPC),它非常適合遠程過程/命令。

你通常有一個端點來接收一堆 XML 數據。例如 SOAP 協議

圖片.png

另一個很好的例子是 Slack API。它稍微多樣化一些,有幾個端點,但它仍然是 RPC 風格的 API。它暴露了 Slack 的各種功能,中間沒有增加任何功能。以下代碼允許你向特定頻道發佈消息。

圖片.png

儘管它是根據 Richardson 的模型是 0 級 API,但這並不意味着它不好。只要它可用並能正確服務於業務需求,它就是一個很棒的 API。

等級 1:資源

要構建一個 1 級 API,你需要在系統中找到名詞,並通過不同的 URL 暴露它們,如下例所示。

圖片.png

/api/books 將帶我進入通用書籍目錄。/api/profile 將帶我進入這些書的作者的個人資料(如果只有一個的話)。要獲取資源的第一個具體實例,我在 URL 中添加 ID(或其他引用)。

我還可以在 URLs 中嵌套資源,並顯示它們是如何層次化組織的。

回到 Slack 的例子,這是它作為 1 級 API 的樣子:

圖片.png

URL 發生了變化;現在我們有了/api/channels/general/messages 代替/api/chat.postMessage。

“channel”部分的信息已從正文移到URL中。這確實表明使用這個 API,你可以期待將消息發佈到 general 頻道。

等級 2:HTTP 動詞

一個 2 級 API 利用 HTTP 動詞添加更多的含義和意圖。這些動詞有很多,我只使用一小部分基本的:PUT / DELETE / GET / POST。

使用這些動詞,我們期望含有它們的 URLs 展現不同的行為:

  • POST—創建新數據
  • PUT—更新現有數據
  • DELETE—移除數據
  • GET—尋找特定 id 的數據輸出,或獲取資源(或整個集合)

或者,使用之前的 /api/books 示例:

圖片.png

“安全”和“冪等”的含義是什麼?

“安全”的方法是不會改變數據的方法。REST 建議 GET 只應該用來獲取數據,因此它是上述集合中唯一的安全方法。不論你調用一個基於 REST 的 GET 方法多少次,它都不應該在數據庫中改變任何東西。但這並不固有於動詞——這取決於你如何實現它,所以你需要確保這一點。所有其他方法將以不同的方式改變數據,不能隨機使用。在 REST 中,GET 既是安全的也是冪等的。

一個“冪等”的方法是在多次使用中不會產生不同結果的方法。根據 REST 的説法,DELETE 應該是冪等的——如果你一次刪除一個資源,然後再次調用 DELETE 該資源,它不應改變任何東西。資源應該已經消失了。POST 是 REST 規範中唯一的非冪等方法,所以你可以多次 POST 同一個資源,你會得到重複項。

讓我們重新審視 Slack 的例子,看看如果我們在其中使用 HTTP 動詿進行更多操作會是什麼樣子。

圖片.png

我們可以使用 POST 向 general 頻道發送消息。我們可以使用 GET 從 general 頻道獲取消息。我們可以使用 DELETE 刪除具有特定 ID 的消息——這變得有趣了,因為消息不與特定頻道綁定,所以我可能需要設計一個單獨的 API 來移除消息。這個例子展示了設計 API 並不總是容易的;有很多選擇和權衡要做。

等級 3:HATEOAS

還記得只有文本的計算機遊戲,沒有任何圖形嗎?你只有很多描述你在哪裏,以及你接下來能做什麼的文本。要進展,你必須鍵入你的選擇。HATEOAS 就有點像這樣。

圖片.png

HATEOAS 代表“應用程序狀態的超媒體引擎”(Hypermedia as the Engine of Application State)

當你有了 HATEOAS,每當有人使用你的API時,他們可以看到他們還可以用它做什麼。HATEOAS 回答了“我接下來可以去哪裏?”的問題。

圖片.png

但這還不是全部。HATEOAS 還可以對數據關係進行建模。我們可以擁有一個資源,URL 中不嵌套作者,但我們可以發佈鏈接,所以如果有人對作者感興趣,他們可以去那裏探索。

這不像成熟度模型的其他級別那樣流行,但有些開發者使用它。例如 Jira,下面是他們搜索 API 的一部分:

圖片.png

他們嵌套了你可以探索的其他資源的鏈接,以及這個問題的轉換列表。他們的 API 很有趣,因為它在頂部有一個“擴展”參數。它允許你選擇你不想要鏈接的字段,而是選擇完整內容。

使用 HATEOAS 的另一個例子是 Artsy。他們的 API 嚴重依賴 HATEOAS。他們還使用 JSON Plus 調用規範,這為鏈接結構制定了特殊的約定。下面是使用 HATEOAS 進行分頁的一個例子,這是使用 HATEOAS 的最酷的例子之一。

圖片.png

你可以提供指向下一個、上一個、第一個、最後一個頁面的鏈接,以及你認為必要的其他頁面的鏈接。這簡化了 API 的使用,因為你不需要在客户端添加URL解析邏輯,或者添加分頁號的方式。你只需得到已經結構化好的鏈接的客户端就可以使用了。

什麼構成了一個好的 API

到此為止 Richardson 的模型,但這並不是構成好API的全部。其他重要的質量是什麼呢?

錯誤/異常處理

我期待從我使用的 API 中得到的一個基本的東西是,需要有一個明顯的方式來告訴我是否有錯誤或異常。我需要知道我的請求是否已處理。

瞧,HTTP 還有一種簡單的方式來做到這一點:HTTP 狀態碼。

控制狀態代碼的基本規則是:

  • 2xx 表示正常
  • 3xx 表示你要找的公主在另一個城堡——你要找的資源在另一個地方
  • 4xx 表示客户端做了一些錯誤的事情
  • 5xx 表示服務器失敗
  • 500 內部服務器錯誤 - 小貓咪梗

圖片.png

至少,你的 API 應該提供 4xx 和 5xx 狀態碼。5xx 有時是自動生成的。例如,客户端向服務器發送某些東西,它是一個無效請求,驗證有缺陷,問題沿着代碼下發,我們有一個異常——它將返回一個 5xx狀 態碼。

如果你想要致力於使用特定的狀態碼,你會發現自己在想,“哪個代碼最適合這種情況?”這個問題並不總是容易回答。

我建議你去查閲 RFC,它規定了這些狀態碼,比其他來源提供更廣泛的解釋,告訴你這些代碼什麼時候合適等等。幸運的是,有幾個在線資源可以幫助你選擇,比如 Mozilla 的 HTTP 狀態碼指南。

文檔

偉大的 API 擁有偉大的文檔。文檔的最大問題通常是找人來更新它,隨着 API 的增長。一個很好的選擇是自我更新的文檔,它與代碼沒有脱節。

例如,註釋與代碼無關。代碼改變時,註釋保持不變,變得過時。它們可能比沒有註釋還糟糕,因為過一段時間後它們將提供錯誤的信息。註釋不會自動更新,所以開發者需要記得與代碼一起維護它們。

自我更新文檔工具解決了這個問題。一個流行的工具 Apifox 可以高效的幫助你解決問題。

圖片.png

可緩存性

在某些系統中,可緩存性可能不是大問題。你可能沒有很多可以緩存的數據,一切都在不斷變化,或者你可能沒有很多流量。

但在大多數情況下,可緩存性對於良好的性能至關重要。它與 RESTful API 相關,因為HTTP協議與緩存有很多關係,例如 HTTP 頭允許你控制緩存行為。

你可能希望在客户端緩存東西,或者在你的應用程序中緩存,如果你有一個註冊表或值存儲來保存數據。但 HTTP 允許你幾乎免費獲得良好的緩存,所以如果可能的話——不要錯過免費的午餐。

圖片.png

此外,由於緩存是 HTTP 規範的一部分,很多參與 HTTP 的東西都會知道如何緩存東西:瀏覽器,它們天生支持緩存,以及你和客户端之間的其他中間服務器。

進化的 API 設計

構建 API 和現代軟件的最重要部分是適應性。沒有適應性,開發時間會減慢,尤其是在面對截止日期時,推出功能變得更加困難。

“軟件架構”在不同的上下文中意味着不同的東西,但就目前而言,讓我們採納這個定義:

軟件架構:避開阻礙未來變更的決策的行為/藝術。

考慮到這一點,當你設計你的軟件並必須在具有相似好處的選項之間選擇時,你應始終選擇更具未來性的那一個。

好的實踐並不是一切。以正確的方式構建錯誤的東西並不是你想要的。更好的是採納成長的心態並接受變化是不可避免的,尤其是如果你的項目將繼續增長的話。

為了讓您的 API 更具適應性,其中一個關鍵做法是保持API層的輕便。真正的複雜性應該下放。

API 不應該決定實現

一旦你發佈一個公共 API,它就是固定的,你不能更改它。但如果你別無選擇,只能承諾一個設計得不夠好的 API 怎麼辦?

你應該始終尋找簡化實現的方法。有時,用一個特殊的 HTTP 頭來控制你的 API 的響應格式可能是一個比構建另一個 API 並稱之為 v2 更簡潔的解決方案。

API 只是另一層抽象。它們不應該決定實現。有幾種開發模式可以幫助你避免這個問題。

API 網關

這是一種外觀模式開發模式。如果你將一個單體分解成一堆微服務,並想向世界公開一些功能,你只需建立一個 API 網關,它就像一個外觀一樣。

它將為不同的微服務(可能具有不同的 API,使用不同的錯誤格式等)提供一個統一的接口。

針對前端的後端

如果你需要構建一個 API 來滿足幾種不同的客户端,這可能會很困難。為一個客户做出的決策會影響其他客户的功能。

針對前端的後端説——如果你有不同的客户喜歡不同的 API,比如移動應用喜歡 GraphQL,那就為他們建立 API。

這隻有在你的 API 是一個抽象層,並且很薄的情況下才有效。如果它與你的數據庫耦合,或者太大,邏輯太多,你就無法做到這一點。

GraphQL 與 RESTful

GraphQL 有很多炒作。它是新來的,但已經吸引了許多粉絲。以至於一些開發者聲稱它將取代 REST。

儘管 GraphQL 相對於 RESTful 規範來説較新,但它們有很多相似之處。GraphQL 的最大缺點是可緩存性——它必須在客户端或應用程序中實現。有客户端庫具備內建的緩存能力(如 Apollo),但這比利用 HTTP 提供的幾乎免費的緩存能力更難。

技術上講,GraphQL 處於 Richardson 模型的 0 級,但它具有良好 API 的特性。你可能無法使用幾項 HTTP 功能,但 GraphQL 旨在解決特定問題。

GraphQL 在合併不同API並將它們作為一個 GraphQL API 公開時表現出色。

圖片.png

GraphQL 在處理欠抓取和過度抓取方面表現出色,這是 REST API 可能難以管理的問題。這兩者都與性能相關——如果你欠抓取,你沒有有效地使用 API 調用,所以你必須進行很多調用。當你過度抓取時,你的調用導致的數據傳輸比必要的更大,這是帶寬浪費。

REST 與 GraphQL 的比較是一個很好的過渡,總結了一個好 API 的最重要特徵。

  • 詳細瞭解:GraphQL vs RESTful API:如何選擇?

圖片.png

好的API特性

  • 你需要清晰表示數據——RESTful 通過資源的形式為你提供這一點。
  • 你需要展示哪些操作可用——RESTful 通過結合資源與 HTTP 動詞做到這一點。
  • 需要有一種確認是否存在錯誤/異常的方法——HTTP 狀態碼可以做到這一點,可能還有解釋它們的響應。
  • 有可發現性和導航的可能性很好——在 RESTful 中,HATEOAS 負責這一點。
  • 擁有出色的文檔很重要——在這種情況下,可執行的、自更新的文檔可以處理這個問題,這超出了 RESTful 規範的範疇。
  • 最後但同樣重要的是——偉大的 API 應該具備可緩存性,除非你的特定情況表明這不是必需的。

REST 與 GraphQL 之間最大的區別是它們處理緩存的方式。當你按照 REST 方式構建你的 API 時,你幾乎可以免費獲得 HTTP 緩存。如果你選擇 GraphQL,你需要擔心在客户端或你的應用程序中添加緩存。

  • 源於:https://stxnext.com/blog/how-to-build-a-good-api-that-wont-em...
user avatar toopoo 头像 cyzf 头像 haoqidewukong 头像 yinzhixiaxue 头像 nihaojob 头像 dirackeeko 头像 linx 头像 xiaolei_599661330c0cb 头像 gaoxingdehongshaorou_clgwsy 头像 woniuseo 头像 nqbefgvs 头像 weidewei 头像
点赞 87 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.