如何設計一個良好的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就已經非常好了。

2、URI

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

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

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

舉個例子,要查看用戶編碼是101的用戶信息,我可以定義get的請求方式,而他的版本是V1,資源名稱是users,資源ID是1001。

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

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

舉個例子,要查看用戶編碼是101的用戶的權限信息,我可以定義get的請求方式。而他的版本是V1,主資源名稱是Users,主資源ID是1001子資源名稱是Roles,資源ID是101。

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

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

3、錯誤碼和返回機制

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

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

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

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

當Restful API接口出現非200的HTTP錯誤碼響應時,可以採用全局的異常結構響應信息。

4、返回體結構

這裏列出了最爲常用的幾個字段,講一下它們各自表示的含義。

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

  • 而message字段用來表示錯誤的摘要信息,它的作用是讓開發人員能快速識別錯誤。

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

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

如果是單條數據,則返回一個對象的json字符串;如果是列表數據,則返回一個封裝的結構體,其中涵蓋count字段和item字段。

count字段表示返回數據的總數據量。需要注意的是,如果接口沒有分頁的需求,儘量不要返回這個count字段,因爲查詢總數據量是耗性能的操作。

此外,item字段表示返回數據列表,他是一個json字符串的數組。

5、小結

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

兼容性

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

例如:

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

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

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

舉個例子,針對要查看用戶編碼是1001的用戶信息,可以分別定義V1和V2兩個版本的API接口,然後分別讓他們對應兩套不完全兼容的業務邏輯特性。

抽象性

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

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

簡單性

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

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

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

性能

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

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

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

在上面這個代碼案例中,要求調用方必填taskId和caseId來保證數據庫索引的使用,以進一步保證服務提供方的服務性能。

總結

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

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

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

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

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

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

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章