如何設計一個良好的API介面?

張飛洪[廈門]發表於2022-04-23

溝通創造價值,分享帶來快樂。這裡是程式設計師閱讀時間,每天和你分享讀書心得,歡迎您每天和我一起精進。今天和大家一起討論的話題是如何設計一個良好的API介面?

作者:樑桂釗

解讀:張飛洪

挑戰

API是軟體系統的核心,而我們在設計API介面的時候會面臨著非常多的挑戰:

  • 場景上來看,它是多樣的,如何設計一個隨處適用的API?

  • 我們所參與的業務不斷演進的,如何設計一個有相容性的API?

  • 我們的軟體流程是協同開發的,那我們如何實現對API的統一認知

今天我想和大家探討一下如何設計一個良好的API介面,我覺得好的API設計需要同時考慮到這幾個要素:標準化、相容性、抽象性、簡單性、高效能,可以說這幾個要素缺一不可。

如何設計一個良好的API介面?

標準化

對於Web API標準化而言,一個非常好的案例就是Restful API。目前業界的Open API多數是基於Restful API規範設計的。

1、等級模型

需要注意的是Restful API它具有成熟度的模型。

  • 其中Level 0是普通的請求響應模式。

  • Level 1引入了資源的概念,各個資源可以單獨建立URI,與Level 0相比,它通過資源分而治之的方法來處理複雜問題。

  • Level 2引入了一套標準的HTTP協議,它通過遵守HTTP協議定義的動詞並配合HTTP響應狀態碼來規範化Web API的標準。

  • Level 3中,使用超媒體可以使協議擁有自我描述的能力。

通常情況下,成熟度模型中達到Level 2就已經非常好了。

如何設計一個良好的API介面?

2、URI

在Restful API中,每一個URI代表著一種資源,是每一個資源的唯一定位符。所謂資源,它可以是伺服器上的一段文字、一個檔案、一張圖片、一首歌曲,或者是一種服務。

如何設計一個良好的API介面?

Restful API呢,規定了通過get/post/put/patch/delete等方式對服務端的資源進行操作。

因此,我們在定義一個Web API的時候,需要明確定義出它的請求方式、版本、資源名稱和資源ID。

如何設計一個良好的API介面?

舉個例子,要檢視使用者編碼是101的使用者資訊,我可以定義get的請求方式,而他的版本是V1,資源名稱是users,資源ID是1001。

如何設計一個良好的API介面?

這裡可以思考一下,如果存在多個資源組合的情況呢?

事實上還可引入子資源的概念,需要明確定義出它的請求方式、版本、資源名稱與資源ID,以及子資源名稱與此資源ID。

如何設計一個良好的API介面?

舉個例子,要檢視使用者編碼是101的使用者的許可權資訊,我可以定義get的請求方式。而他的版本是V1,主資源名稱是Users,主資源ID是1001子資源名稱是Roles,資源ID是101。

如何設計一個良好的API介面?

有時候,當一個自然變化難以使用標準的Restful API來命名時,就可以考慮使用一些特殊的actions命名。

比如密碼修改介面,我可以定義Put的請求方式,而他的版本是V,主資源名稱是users,主資源ID是101資源欄位是password。然後定義一個action的操作是modify。

如何設計一個良好的API介面?

3、錯誤碼和返回機制

與此同時啊,建議不要試圖建立自己的錯誤碼和返回錯誤機制。

很多時候呢,我們覺得提供更多的自定義的錯誤碼有助於傳遞資訊,但其實,如果只是傳遞資訊的話,錯誤資訊欄位可以達到同樣的效果。

此外,對於客戶端來說,很難關注到那麼多錯誤的細節,這樣的設計只會讓API的處理變得更加複雜,難於理解。

因此,我的建議是遵守Restful API的規範,使用HTTP規範的錯誤碼。例如,我們用200表示請求成功,用400表示錯誤的請求,而500則表示伺服器內部的錯誤。

如何設計一個良好的API介面?

當Restful API介面出現非200的HTTP錯誤碼響應時,可以採用全域性的異常結構響應資訊。

4、返回體結構

這裡列出了最為常用的幾個欄位,講一下它們各自表示的含義。

  • 其中code欄位用來表示某類錯誤的錯誤碼,例如前面介紹的無效請求、缺少引數、未授權資源、未找到資源、已存在的錯誤。

  • 而message欄位用來表示錯誤的摘要資訊,它的作用是讓開發人員能快速識別錯誤。

  • server_time欄位,用來記錄傳送錯誤時的伺服器時間,他可以明確的告訴開發人員發生錯誤時的具體時間,便於在日誌系統中根據時間範圍來快速定位錯誤資訊。

如何設計一個良好的API介面?

此外,不常用欄位會根據不同的情況做出有不同的響應。

如果是單條資料,則返回一個物件的json字串;如果是列表資料,則返回一個封裝的結構體,其中涵蓋count欄位和item欄位。

如何設計一個良好的API介面?

count欄位表示返回資料的總資料量。需要注意的是,如果介面沒有分頁的需求,儘量不要返回這個count欄位,因為查詢總資料量是耗效能的操作。

此外,item欄位表示返回資料列表,他是一個json字串的陣列。

5、小結

總結一下,怎麼來理解規範呢?可以說他就是大家約定俗成的標準,如果都遵守這套標準,自然溝通成本也就大大降低了。

相容性

接著我們再來探討一下API介面的相容性。由於我們參與的業務是不斷演進的,設計一個有相容性的API就顯得尤為重要了。如果介面不能夠向下相容,業務就會受到很大影響。

例如:

  • 我們的產品是涵蓋android、ios、pc端的,都執行在使用者的機器上,這種情況下,使用者必須升級產品到最新的版本才能夠更好的使用。

  • 同時,我們還可能遇到服務端不停機升級,由於API不相容而遇到短暫的服務故障。

為了實現API的相容性,我們引入了版本的概念,前面的案例URI中通過保留版本號實現了相容多個版本。

如何設計一個良好的API介面?

舉個例子,針對要檢視使用者編碼是1001的使用者資訊,可以分別定義V1和V2兩個版本的API介面,然後分別讓他們對應兩套不完全相容的業務邏輯特性。

抽象性

通常情況下,我們的介面抽象都是基於業務需求的,因此我們一方面要定義出清晰的業務問題域模型,例如資料模型和領域模型等,並建立起某個問題的現實對映,這樣有利於不同的角色對API設計認知的統一。

另一方面,API設計如果可以實現抽象,就可以很好的遮蔽具體的業務實現細節,為我們提供更好的可擴充套件性

簡單性

簡單性的主要宗旨是遵守最少的知識原則。

怎麼來理解呢?其實就是客戶端不需要知道那麼多服務的API介面,以及這些API介面的呼叫細節,比如設計模式的外觀模式和中介者模式都是它的應用案例。

如何設計一個良好的API介面?

如圖所示,外觀介面將多個服務進行業務封裝與整合,並提供了一個簡單的API呼叫給客戶端使用,這樣設計的好處是什麼呢?就在於客戶端只需要呼叫這個外觀介面就行了,省去了一些繁雜的步驟。

效能

同時,我們還需要關注效能,就比如說外觀介面,雖然保證了簡單性,但是增加了服務端的業務複雜度,同時,由於多服務之間的聚合,導致他們的介面效能也不是太好。

此外,我們還需要考慮欄位的各種組合會不會導致資料庫的效能問題。有時,我們可能暴露了太多欄位給外部使用,導致資料庫沒有相應的索引而發生全表掃描。這種情況在查詢的場景下非常常見,因此我們可以只提供存在索引的欄位組合給外部呼叫。

Result<Void> agree(Long taskId,Long caseId,Configger configger)

在上面這個程式碼案例中,要求呼叫方必填taskId和caseId來保證資料庫索引的使用,以進一步保證服務提供方的服務效能。

總結

今天給大家側重探討的是如何設計一個良好的API介面。

好的API設計需要我們同時考慮到標準化、相容性、抽象性、簡單性和高效能

其中,標準化的關鍵在於儘可能少的建立自定義規範和機制,而是共同遵守業內標準,例如HTTP規範和Restful API規範。

通常情況下,我們會採取版本號來解決多版本的相容性的問題。

抽象性需要確保能夠定義出清晰的問題域模型,儘可能遮蔽具體的業務實現細節。

簡單性是相對的,需要遵守最少知識原則,讓呼叫方儘可能少的知道內部的呼叫細節,效能注意的細節就多了,這裡主要強調了業務組合和引數組合場景。

如何設計一個良好的API介面? 

 

相關文章