API是軟件系統(tǒng)的核心,而我們?cè)谠O(shè)計(jì)API接口的時(shí)候會(huì)面臨著非常多的挑戰(zhàn):
- 場(chǎng)景上來(lái)看,它是多樣的,如何設(shè)計(jì)一個(gè)隨處適用的API?
- 我們所參與的業(yè)務(wù)不斷演進(jìn)的,如何設(shè)計(jì)一個(gè)有兼容性的API?
- 我們的軟件流程是協(xié)同開發(fā)的,那我們?nèi)绾螌?shí)現(xiàn)對(duì)API的統(tǒng)一認(rèn)知?
今天我想和大家探討一下如何設(shè)計(jì)一個(gè)良好的API接口,我覺得好的API設(shè)計(jì)需要同時(shí)考慮到這幾個(gè)要素:標(biāo)準(zhǔn)化、兼容性、抽象性、簡(jiǎn)單性、高性能,可以說(shuō)這幾個(gè)要素缺一不可。
標(biāo)準(zhǔn)化
對(duì)于Web API標(biāo)準(zhǔn)化而言,一個(gè)非常好的案例就是Restful API。目前業(yè)界的Open API多數(shù)是基于Restful API規(guī)范設(shè)計(jì)的。
1、等級(jí)模型
需要注意的是Restful API它具有成熟度的模型。
- 其中Level 0是普通的請(qǐng)求響應(yīng)模式。
- Level 1引入了資源的概念,各個(gè)資源可以單獨(dú)創(chuàng)建URI,與Level 0相比,它通過(guò)資源分而治之的方法來(lái)處理復(fù)雜問(wèn)題。
- Level 2引入了一套標(biāo)準(zhǔn)的HTTP協(xié)議,它通過(guò)遵守HTTP協(xié)議定義的動(dòng)詞并配合HTTP響應(yīng)狀態(tài)碼來(lái)規(guī)范化Web API的標(biāo)準(zhǔn)。
- Level 3中,使用超媒體可以使協(xié)議擁有自我描述的能力。
通常情況下,成熟度模型中達(dá)到Level 2就已經(jīng)非常好了。
2、URI
在Restful API中,每一個(gè)URI代表著一種資源,是每一個(gè)資源的唯一定位符。所謂資源,它可以是服務(wù)器上的一段文本、一個(gè)文件、一張圖片、一首歌曲,或者是一種服務(wù)。
Restful API呢,規(guī)定了通過(guò)get/post/put/patch/delete等方式對(duì)服務(wù)端的資源進(jìn)行操作。
因此,我們?cè)诙x一個(gè)Web API的時(shí)候,需要明確定義出它的請(qǐng)求方式、版本、資源名稱和資源ID。
舉個(gè)例子,要查看用戶編碼是101的用戶信息,我可以定義get的請(qǐng)求方式,而他的版本是V1,資源名稱是users,資源ID是1001。
這里可以思考一下,如果存在多個(gè)資源組合的情況呢?
事實(shí)上還可引入子資源的概念,需要明確定義出它的請(qǐng)求方式、版本、資源名稱與資源ID,以及子資源名稱與此資源ID。
舉個(gè)例子,要查看用戶編碼是101的用戶的權(quán)限信息,我可以定義get的請(qǐng)求方式。而他的版本是V1,主資源名稱是Users,主資源ID是1001子資源名稱是Roles,資源ID是101。
有時(shí)候,當(dāng)一個(gè)自然變化難以使用標(biāo)準(zhǔn)的Restful API來(lái)命名時(shí),就可以考慮使用一些特殊的actions命名。
比如密碼修改接口,我可以定義Put的請(qǐng)求方式,而他的版本是V,主資源名稱是users,主資源ID是101資源字段是password。然后定義一個(gè)action的操作是modify。
3、錯(cuò)誤碼和返回機(jī)制
與此同時(shí)啊,建議不要試圖創(chuàng)建自己的錯(cuò)誤碼和返回錯(cuò)誤機(jī)制。
很多時(shí)候呢,我們覺得提供更多的自定義的錯(cuò)誤碼有助于傳遞信息,但其實(shí),如果只是傳遞信息的話,錯(cuò)誤信息字段可以達(dá)到同樣的效果。
此外,對(duì)于客戶端來(lái)說(shuō),很難關(guān)注到那么多錯(cuò)誤的細(xì)節(jié),這樣的設(shè)計(jì)只會(huì)讓API的處理變得更加復(fù)雜,難于理解。
因此,我的建議是遵守Restful API的規(guī)范,使用HTTP規(guī)范的錯(cuò)誤碼。例如,我們用200表示請(qǐng)求成功,用400表示錯(cuò)誤的請(qǐng)求,而500則表示服務(wù)器內(nèi)部的錯(cuò)誤。
當(dāng)Restful API接口出現(xiàn)非200的HTTP錯(cuò)誤碼響應(yīng)時(shí),可以采用全局的異常結(jié)構(gòu)響應(yīng)信息。
4、返回體結(jié)構(gòu)
這里列出了最為常用的幾個(gè)字段,講一下它們各自表示的含義。
- 其中code字段用來(lái)表示某類錯(cuò)誤的錯(cuò)誤碼,例如前面介紹的無(wú)效請(qǐng)求、缺少參數(shù)、未授權(quán)資源、未找到資源、已存在的錯(cuò)誤。
- 而message字段用來(lái)表示錯(cuò)誤的摘要信息,它的作用是讓開發(fā)人員能快速識(shí)別錯(cuò)誤。
- server_time字段,用來(lái)記錄發(fā)送錯(cuò)誤時(shí)的服務(wù)器時(shí)間,他可以明確的告訴開發(fā)人員發(fā)生錯(cuò)誤時(shí)的具體時(shí)間,便于在日志系統(tǒng)中根據(jù)時(shí)間范圍來(lái)快速定位錯(cuò)誤信息。
此外,不常用字段會(huì)根據(jù)不同的情況做出有不同的響應(yīng)。
如果是單條數(shù)據(jù),則返回一個(gè)對(duì)象的json字符串;如果是列表數(shù)據(jù),則返回一個(gè)封裝的結(jié)構(gòu)體,其中涵蓋count字段和item字段。
count字段表示返回?cái)?shù)據(jù)的總數(shù)據(jù)量。需要注意的是,如果接口沒有分頁(yè)的需求,盡量不要返回這個(gè)count字段,因?yàn)椴樵兛倲?shù)據(jù)量是耗性能的操作。
此外,item字段表示返回?cái)?shù)據(jù)列表,他是一個(gè)json字符串的數(shù)組。
5、小結(jié)
總結(jié)一下,怎么來(lái)理解規(guī)范呢?可以說(shuō)他就是大家約定俗成的標(biāo)準(zhǔn),如果都遵守這套標(biāo)準(zhǔn),自然溝通成本也就大大降低了。
兼容性
接著我們?cè)賮?lái)探討一下API接口的兼容性。由于我們參與的業(yè)務(wù)是不斷演進(jìn)的,設(shè)計(jì)一個(gè)有兼容性的API就顯得尤為重要了。如果接口不能夠向下兼容,業(yè)務(wù)就會(huì)受到很大影響。
例如:
- 我們的產(chǎn)品是涵蓋Android、IOS、pc端的,都運(yùn)行在用戶的機(jī)器上,這種情況下,用戶必須升級(jí)產(chǎn)品到最新的版本才能夠更好的使用。
- 同時(shí),我們還可能遇到服務(wù)端不停機(jī)升級(jí),由于API不兼容而遇到短暫的服務(wù)故障。
為了實(shí)現(xiàn)API的兼容性,我們引入了版本的概念,前面的案例URI中通過(guò)保留版本號(hào)實(shí)現(xiàn)了兼容多個(gè)版本。
舉個(gè)例子,針對(duì)要查看用戶編碼是1001的用戶信息,可以分別定義V1和V2兩個(gè)版本的API接口,然后分別讓他們對(duì)應(yīng)兩套不完全兼容的業(yè)務(wù)邏輯特性。
抽象性
通常情況下,我們的接口抽象都是基于業(yè)務(wù)需求的,因此我們一方面要定義出清晰的業(yè)務(wù)問(wèn)題域模型,例如數(shù)據(jù)模型和領(lǐng)域模型等,并建立起某個(gè)問(wèn)題的現(xiàn)實(shí)映射,這樣有利于不同的角色對(duì)API設(shè)計(jì)認(rèn)知的統(tǒng)一。
另一方面,API設(shè)計(jì)如果可以實(shí)現(xiàn)抽象,就可以很好的屏蔽具體的業(yè)務(wù)實(shí)現(xiàn)細(xì)節(jié),為我們提供更好的可擴(kuò)展性。
簡(jiǎn)單性
簡(jiǎn)單性的主要宗旨是遵守最少的知識(shí)原則。
怎么來(lái)理解呢?其實(shí)就是客戶端不需要知道那么多服務(wù)的API接口,以及這些API接口的調(diào)用細(xì)節(jié),比如設(shè)計(jì)模式的外觀模式和中介者模式都是它的應(yīng)用案例。
如圖所示,外觀接口將多個(gè)服務(wù)進(jìn)行業(yè)務(wù)封裝與整合,并提供了一個(gè)簡(jiǎn)單的API調(diào)用給客戶端使用,這樣設(shè)計(jì)的好處是什么呢?就在于客戶端只需要調(diào)用這個(gè)外觀接口就行了,省去了一些繁雜的步驟。
性能
同時(shí),我們還需要關(guān)注性能,就比如說(shuō)外觀接口,雖然保證了簡(jiǎn)單性,但是增加了服務(wù)端的業(yè)務(wù)復(fù)雜度,同時(shí),由于多服務(wù)之間的聚合,導(dǎo)致他們的接口性能也不是太好。
此外,我們還需要考慮字段的各種組合會(huì)不會(huì)導(dǎo)致數(shù)據(jù)庫(kù)的性能問(wèn)題。有時(shí),我們可能暴露了太多字段給外部使用,導(dǎo)致數(shù)據(jù)庫(kù)沒有相應(yīng)的索引而發(fā)生全表掃描。這種情況在查詢的場(chǎng)景下非常常見,因此我們可以只提供存在索引的字段組合給外部調(diào)用。
Result<Void> agree(Long taskId,Long caseId,Configger configger)
在上面這個(gè)代碼案例中,要求調(diào)用方必填taskId和caseId來(lái)保證數(shù)據(jù)庫(kù)索引的使用,以進(jìn)一步保證服務(wù)提供方的服務(wù)性能。
總結(jié)
今天給大家側(cè)重探討的是如何設(shè)計(jì)一個(gè)良好的API接口。
好的API設(shè)計(jì)需要我們同時(shí)考慮到標(biāo)準(zhǔn)化、兼容性、抽象性、簡(jiǎn)單性和高性能。
其中,標(biāo)準(zhǔn)化的關(guān)鍵在于盡可能少的創(chuàng)建自定義規(guī)范和機(jī)制,而是共同遵守業(yè)內(nèi)標(biāo)準(zhǔn),例如HTTP規(guī)范和Restful API規(guī)范。
通常情況下,我們會(huì)采取版本號(hào)來(lái)解決多版本的兼容性的問(wèn)題。
抽象性需要確保能夠定義出清晰的問(wèn)題域模型,盡可能屏蔽具體的業(yè)務(wù)實(shí)現(xiàn)細(xì)節(jié)。
簡(jiǎn)單性是相對(duì)的,需要遵守最少知識(shí)原則,讓調(diào)用方盡可能少的知道內(nèi)部的調(diào)用細(xì)節(jié),性能注意的細(xì)節(jié)就多了,這里主要強(qiáng)調(diào)了業(yè)務(wù)組合和參數(shù)組合場(chǎng)景。
文章來(lái)自
https://www.cnblogs.com/jackyfei/p/16182421.html