RESTful API接口設(shè)計(jì)規(guī)范與最佳實(shí)踐
當(dāng)前位置:點(diǎn)晴教程→知識管理交流
→『 技術(shù)文檔交流 』
Part1介紹RESTFull 接口設(shè)計(jì)目前廣泛應(yīng)用于各種軟件系統(tǒng)中,特別是前后端分離架構(gòu)的web應(yīng)用。相信各位web應(yīng)用的開發(fā)者對這個概念并不陌生,但是我們經(jīng)常會遇到幾個這樣的疑惑或者問題:
在我們試圖搞清楚以上幾個問題之前,首先需要讀者了解或者閱讀過關(guān)于RESTfull的定義,這類定義百度一搜一大把這里就不重復(fù)贅述了。接著是最好你實(shí)踐過這類風(fēng)格設(shè)計(jì)的接口,如果你心中也同樣有這幾個問題或者疑問那就更好了,當(dāng)然最后這兩點(diǎn)要求并不是必須。 如果你已經(jīng)閱讀過關(guān)于RESTfull的相關(guān)定義,你就會發(fā)現(xiàn)RESTfull是一種接口設(shè)計(jì)風(fēng)格,它制定了一些原則條件,只要你遵守了,就算是RESTful風(fēng)格的接口設(shè)計(jì)。 那么問題就來了,這里面就存在很多靈活空間了,比如說我部分遵守,部分不遵守,可以嗎?可以。或者說我在遵守的基礎(chǔ)上,再自定義一些行為,可以嗎?可以。 各種諸如此類的實(shí)踐路線導(dǎo)致了我們很難在開發(fā)生涯中真的看到有兩個或更多的接口實(shí)現(xiàn)了一模一樣的RESTfull風(fēng)格接口,即便他們的業(yè)務(wù)是一樣的。這是因?yàn)镽ESTfull本身既然是一種設(shè)計(jì)風(fēng)格,那么風(fēng)格發(fā)揮的主動權(quán)自然就是在開發(fā)者身上,而且絕大多數(shù)的項(xiàng)目所開發(fā)的API接口都是對內(nèi)或者有限對外開放的,所以對于RESTfull的實(shí)踐是否合格更多取決于內(nèi)部團(tuán)隊(duì)老大的看法。
這里我個人覺得有一部分原因是同行襯托,RESTfull基于HTTP協(xié)議,采用json格式的字符串作為傳輸內(nèi)容,相對于過去的SOAP協(xié)議,采用XML格式標(biāo)記語言來說,RESTfull無論從開發(fā)成本或者網(wǎng)絡(luò)傳輸來說都顯得輕量太多太多。而前面提到的,關(guān)于實(shí)際開發(fā)出來的RESTfull接口風(fēng)格迥異的問題實(shí)際上并沒有太糟糕,為什么這么說呢?因?yàn)樽钇鸫a的一點(diǎn)是無論實(shí)際設(shè)計(jì)出來的接口再奇葩,總歸是基于HTTP協(xié)議和使用JSON字符串來傳遞數(shù)據(jù),這最起碼保證了我們在調(diào)用別人設(shè)計(jì)好的接口的時候足夠簡單。 當(dāng)然,能調(diào)用跟實(shí)際交互還有一段很長的距離,而中間這個過程你是否舒適,有一部分就體現(xiàn)在接口細(xì)節(jié)設(shè)計(jì)上了。按照一般的經(jīng)驗(yàn),像這種”標(biāo)準(zhǔn)化“的設(shè)計(jì),我們會封裝一些基礎(chǔ)方法來實(shí)現(xiàn)接口的調(diào)用和數(shù)據(jù)接收,但現(xiàn)實(shí)卻是無法實(shí)現(xiàn)的。因?yàn)镽ESfull接口的具體實(shí)現(xiàn)細(xì)節(jié)上是因人而異的,這就導(dǎo)致了我們封裝的調(diào)用或者解析代碼未必能夠完全復(fù)用,很典型的例子就是我們一開始拋出來的那幾個問題。 這時候讀者們肯定想說,還是想吐槽,是的,我們可以吐槽一個接口設(shè)計(jì)得很糟心,讓我們調(diào)用起來很難受,但是我們又不可否認(rèn)他確實(shí)遵守了RESTfull的基本規(guī)定,你可以發(fā)送一個HTTP請求,通過JSON來提交和接收數(shù)據(jù),你完全拿對方?jīng)]辦法。所以這也就是為什么我們一開始給出的答案是:沒有對錯。我們可以吐槽一個接口設(shè)計(jì)得非常糟糕,但是不能說這個接口不是RESTfull接口,但是,我們可以評判一個接口是否嚴(yán)格遵循了RESTfull風(fēng)格設(shè)計(jì)以及遵循的程度有多高。 我們可以從開局的幾個問題入手來嘗試評判下相應(yīng)的接口設(shè)計(jì)是否很好的遵循了RESTfull風(fēng)格設(shè)計(jì)。 Part2為什么接口只設(shè)計(jì)了GET和POST兩種請求方法類型?
從上面的表格可以看出,不同類型的請求方法有著自己明確的含義,在理想的情況下,我們可以通過一個請求類型+請求地址的形式,直觀的看出一個接口的作用,比如:
這里讀者可以嘗試做一個閱讀理解。 那么這里問題就來了,既然HTTP的請求方法類型有助于我們理解一個接口的作用,為什么在有些接口中唯獨(dú)只會使用GET和POST呢?這里面我覺得原因有很多,有些可能我也想不到也猜不到,但是我從個人開發(fā)經(jīng)驗(yàn)上嘗試猜測一下。
坦白說,除了查詢請求這種無可爭議的使用GET之外,其他的全部歸為POST無疑是一件很方便的事。你不需要花時間去考慮接口的行為然后決定要定義成什么請求方法類型,反正具體的實(shí)現(xiàn)邏輯都是一樣的,而且POST方法的描述也似乎能涵蓋到其他幾個類型的請求方法。但這里讀者可能會說,在某些場景下會有歧義,比如說我們要調(diào)用一個接口實(shí)現(xiàn)刪除一個用戶:
這里我們復(fù)用了前面其中一道閱讀理解題并把類型改成了POST。這里第一眼看上去確實(shí)不能很好的表達(dá)接口的意圖,但是我們有接口文檔呀,我在相應(yīng)的接口名稱中寫清楚再放大字體說這個接口是刪除用戶用的不就完事了?這么一聽好像也有道理。所以綜合看來,細(xì)分各個方法請求類型似乎變成一件很多余的事,吃力不討好,干脆就GET/POST一把梭了。 說到這里,我們再回過頭來看看問題本身,做錯了嗎?沒有。那嚴(yán)格遵循了RESTfull風(fēng)格設(shè)計(jì)了嗎,那倒是并沒有。
遵循 RESTfull
不遵循RESTfull
從這里的示例可以看出,在不遵循RESTfull風(fēng)格設(shè)計(jì)的情況下我們難免需要在接口URL地址中增加一些描述性的單詞,這會導(dǎo)致路由接口地址變得很冗長和不夠優(yōu)雅,當(dāng)然如果你覺得這不是什么問題那也是沒錯的,對,你沒錯。
Part3為什么接口是否請求成功,HTTP狀態(tài)碼永遠(yuǎn)只會是200?
從上面表格可以看出,HTTP碼是用于標(biāo)識本次請求響應(yīng)的結(jié)果狀態(tài),通過HTTP狀態(tài)我們可以直觀的判斷出本請求是不是成功的,但是為什么有些接口設(shè)計(jì)的情況是無論成功與否都只會返回200的狀態(tài)碼呢?
首先假設(shè)我們把所有請求響應(yīng)的HTTP狀態(tài)碼都標(biāo)識為200,那么我們必然需要在響應(yīng)內(nèi)容中增加一些字段來描述本次錯誤,例如:
這里大家可能會覺得,我們已經(jīng)有code和message字段來描述本次請求的錯誤了,完全不需要HTTP狀態(tài)碼。這里乍一看是沒有問題的,前端也能在統(tǒng)一異常處理的層面很好的捕獲異常。 但是,當(dāng)你的系統(tǒng)到了一定的規(guī)模(這個很容易達(dá)到,并不要求需要多大的規(guī)模體量,只要不是demo項(xiàng)目),你的錯誤類型就會有很多種,往往我們的錯誤碼清單會很長,當(dāng)然這對于后端開發(fā)來說不是啥問題,因?yàn)檫@個信息其實(shí)是給前端開發(fā)者處理的,但是前端開發(fā)者在處理這些錯誤的時候就難受了。 難受在哪?假設(shè)我們現(xiàn)在有10個關(guān)于賬戶異常的錯誤碼,10個關(guān)于業(yè)務(wù)A的錯誤碼,10個關(guān)于業(yè)務(wù)B的錯誤碼,一共30個。這里面有個業(yè)務(wù)需求,就是某些特定的錯誤碼需要前端做出特定的行為,比如說跳轉(zhuǎn)到指定頁面,或者強(qiáng)制退出啥等等。那么這時候前端在統(tǒng)一異常處理的時候咋做?那就是各種 看起來似乎也問題不大嘛,是的,接著我們需求變了,和原來不一樣了,原來的分支判斷條件可能不適用了,錯誤碼改了,含義不一樣了,或者又增加了30個新的錯誤碼,那么這時候前端開發(fā)者就炸了。
比如說給后端傳遞了錯誤的參數(shù),這種一般后端在校驗(yàn)不通過的時候,會返回的HTTP狀態(tài)碼是400。這類提示信息是需要把具體錯誤信息展示給用戶作為警告提示的,那么前端開發(fā)者在統(tǒng)一異常處理的時候,只需要判斷HTTP狀態(tài)碼是不是400,是的話直接把具體內(nèi)容以各種彈出提示的形式展示即可,不用關(guān)心具體的錯誤碼又是什么(需要特殊處理的除外)。還有一種是401和**403 **HTTP狀態(tài)碼的錯誤,這兩種都是跟權(quán)限有關(guān)的錯誤,前端開發(fā)者在做統(tǒng)一異常處理的時候也可以進(jìn)行針對性的統(tǒng)一捕獲處理。
相對于單純依靠錯誤碼,HTTP狀態(tài)碼+錯誤碼的方式讓前端開發(fā)者更容易實(shí)現(xiàn)封裝和統(tǒng)一處理,前端開發(fā)者根據(jù)HTTP狀態(tài)碼定義不同的響應(yīng)處理,可以大大減少開發(fā)工程量和降低溝通成本。但是這里讀者們可能會說,我們可以把錯誤碼按范圍劃分,實(shí)現(xiàn)比如1~99是代表XX類型錯誤,100~199是XX類型錯誤。這個確實(shí)可以,但是這等于是換了條路開倒車,其實(shí)還是會有一開始的提到的痛點(diǎn)問題出現(xiàn)。而且錯誤碼因?yàn)槭菆F(tuán)隊(duì)定義的,如果維護(hù)不善會導(dǎo)致各種前后端開發(fā)者信息不同步的問題,既然通過HTTP狀態(tài)碼的定義就能解決大部分問題了為什么不用呢? 最后總結(jié)一下這個問題就是,強(qiáng)烈建議嚴(yán)格按照HTTP狀態(tài)碼的定義區(qū)分接口響應(yīng)的HTTP狀態(tài)碼,錯誤碼作為一種細(xì)分的補(bǔ)充。 Part4HTTP狀態(tài)碼不存在,返回 200 還是 404 ?問題: 當(dāng)一個查詢的結(jié)果為空的時候,為什么有的接口設(shè)計(jì)會返回異常(HTTP狀態(tài)碼404或其他),有的則是會返回請求成功(HTTPS狀態(tài)碼200),但是返回結(jié)果是空數(shù)組或者null等表示結(jié)果為空的標(biāo)識? 解析:這個問題情況有點(diǎn)特殊,理論上來說,當(dāng)我們查詢了資源然后結(jié)果是不存在的時候,這個時候用404的HTTP狀態(tài)碼來標(biāo)識本次請求的響應(yīng)狀態(tài)是一點(diǎn)問題都沒有的,也是非常規(guī)范的做法。但是這是建立在業(yè)務(wù)場景規(guī)定,查詢結(jié)果為空的時候?qū)儆诋惓5那疤嵘稀?/p> 1返回HTTP狀態(tài)碼 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |