閱讀本文大概需要 4.6 分鐘。
本文來自
https://juejin.im/post/59eafab36fb9a045076eccc3
前言
隨著網際網路高速發展,公司對專案開發週期不斷縮短,我們面對各種需求,使用原有對接方式,各端已經很難快速應對各種需求,更難以提高效率。於是,我們不得不重新制定對接規範、開發邏輯以便快速上線專案。
我們的目標
-
儘可能的縮小溝通的成本,開最少的會,確定大部分的事。
-
花最少的時間寫文件,保證90%的開發人員看懂所有內容。
-
哪怕不看文件,也能知道各種介面邏輯。
-
不重複寫程式碼
-
儘可能的寫高可讀性的程式碼
我們做了哪些事
-
完成了前後端分離
-
Android、ios、web共用一套介面
-
統一介面規範(post、put、get、patch、delete)
-
統一了除錯工具
-
統一了介面文件
之前的我們
介面是這樣子的:
介面地址 | 含義 | 請求方式 |
…/A專案/模組1/getProducts |
獲得產品 |
GET |
…/A專案/模組1/addProduct |
新增產品 |
POST |
…/A專案/模組1/getProductDetail |
獲得產品詳情 |
GET |
…/A專案/模組1/editProduct |
修改產品 |
POST |
客戶端請求是這樣的:
-
…/A專案/模組1/getProducts?id=1&a=2&b=3&c=4&d=5…………
-
A頁面=====》B頁面(攜帶n個變數)====》C頁面(攜帶m個變數,包含i個A頁面的變數) -------經常n>4
-
大部分請求是POST,至於put、patch、delete是什麼鬼,關我屁事。
-
關於介面入參使用json,那完全是看開發心情。
出參是這樣的:
{"message":"success","code":0,"data":具體內容}
其中data裡包含陣列可能是
[{"a":"1","b":"1"},{"a":"1","b":"1"},{"a":"1","b":"1"},{"a":"1","b":"1"}]
即使下一個頁面用到也不會使用id,而是把所有欄位都傳進去。
A介面中,返回產品用product;B介面中使用good,多個介面很可能不統一。
客戶端對接是這樣子的:
-
安卓、ios一套;部分介面各自用一套;html5端一套。
-
客戶端和後臺是不停交流的
介面文件是這樣的
img
img
-
swagger
-
阿里的rap
-
Word文件
-
其它
當然了,我覺得swagger和rap神器都是非常強大的,能夠實現各種功能邏輯,但是考慮到開發人員掌握程度不通,複雜度較高,難以提高效率,我決定初期並不使用這兩樣神器。
後端是這樣的
…/A專案/模組1/getProducts ----介面
…/A專案/模組1/Products.html ----頁面
…/A專案/模組1/Products.js ----靜態資源
介面和靜態資源纏在一塊,畢竟很多頁面可能是一位開發人員同時開發前端、後端,這裡的弊端是,只需要自己清楚邏輯,很多做法臨時應付,方案並不優雅,別人也很難看懂。一旦這位同事離職,很多說不清的邏輯就留給後人採坑了。
等等…………
重構
下面步入正題,面對以上眾多問題聊聊我是如何重新制定流程的
資料庫約定
約定資料庫裡所有表必須包含名為id主鍵欄位。
可能有人會說,正常來說不是每張表裡都應該有id主鍵嗎?但是,我們專案中由於之前開發不嚴謹,部分表沒有id主鍵,或者不為id的主鍵。這裡我們採用分散式的全球唯一碼來作為id。
api出參約定
約定所有出參裡含list,且其他請求會用到這組list,則list裡所有物件必須含id唯一標識。
入參約定
約定token身份認證統一傳入引數模式,後端採用aop切面程式設計識別使用者身份。其他引數一律為json。
resultfull介面約定
首先我們選擇一個名詞複數,比如產品
post方法
新增一條XXX
比如 ……/products 則代表新增一條產品
入參json如下:
{ "name":"我是一款新產品", "price":100, "kind":"我的分類", "pic":[一組圖片], 等等還有很多}
java 程式碼control層
@ResponseBody @RequestMapping(value = "/A專案/B模組/products", method = {RequestMethod.POST}) public ResultObject getProducts() { //具體邏輯。 }
put方法
新增某條XXX記錄
比如 ……/products/1111111111
入參json如下:
{ "name":"我是一款新產品", "price":100, "kind":"我的分類", "pic":[一組圖片], 等等還有很多}
表示增加一條1111111111id的記錄
java程式碼control層
@ResponseBody @RequestMapping(value = "/A專案/B模組/products/{id}", method = {RequestMethod.PUT}) public ResultObject putProducts(@PathVariable(value = "id") String id) { //具體邏輯。 }
get方法
獲得所有XXX
……/products 則代表獲取所有產品
因為有分頁,所以我們後面加了?page=1&pageSize=50
我們約定了所有名詞複數,都會返回list,且list每個物件都有欄位為id的唯一id。
比如
{ "data":{"list":[{"id":"唯一id","其他很多欄位":""},{"id":"唯一id","其他很多欄位":""}],"page":1,其他欄位}, "code":0, "message":"成功"}
……/products/{id} 獲取某個具體產品(一定比列表更詳細)
比如某個具體產品裡還包含一個list,如該產品推薦列表,則:
……/products/{id}/recommendations
假設它包含的不是一個list,而是物件,比如產品佣金資訊,則:
……/products/{id}/Commission
這裡我們以是否名詞複數來判斷是物件還是list.
java程式碼control層
@ResponseBody @RequestMapping(value = "/A專案/B模組/products/{id}", method = {RequestMethod.GET}) public ResultObject putProducts(@PathVariable(value = "id") String id) { //具體邏輯。 }
patch 方法
更新區域性XXX產品YYY資訊
入參是post方法時入參的子集,所有支援更新的引數會說明,並不是支援所有變數
……/products/{id}
{ "name":"我是一款新產品", "price":100, 部分變數}
java程式碼control層
@ResponseBody @RequestMapping(value = "/A專案/B模組/products/{id}", method = {RequestMethod.PATCH}) public ResultObject putProducts(@PathVariable(value = "id") String id) { //具體邏輯。 }
delete方法
刪除XXX記錄
……/products/11111
刪除11111產品。
java程式碼control層
@ResponseBody @RequestMapping(value = "/A專案/B模組/products/{id}", method = {RequestMethod.DELETE}) public ResultObject putProducts(@PathVariable(value = "id") String id) { //具體邏輯。 }
其他說明
我們儘可能少的使用動詞,但有一些行為需要使用動詞,比如登入等。
關於版本號,我們打算在模組後增加/v1/
等標識。
許可權約定
服務端要對使用者角色進行判斷,是否有許可權執行某個邏輯。
前後端分離約定
後端以開發介面為主,不再參與頁面開發,或者混合式jsp頁面開發,統一以介面形式返回,前端通過js渲染資料以及處理邏輯。
共用介面
web、Android、ios使用統一介面,不在因為哪方特殊要求額外開放介面。
使用統一dao層生成工具
基於mybatis-generator改造成適合我們專案的dao層以及部分service層,內部共同維護共同使用。
使用postman最為介面文件、除錯工具
雖然有上文中介紹的rap和swagger都是特別牛的介面神器,但是我們還是選擇了postman,開發人員將介面名稱、說明、入參、出參,以及各種出參示例都儲存,這樣開發直接可以看得清介面含義。
我們建議使用瀏覽器外掛,這裡以360極速瀏覽器為例。
外掛下載地址:https://download.csdn.net/download/qq273681448/10033456
開啟360瀏覽器擴充套件中心,然後勾選開發者模式,再點選載入已解壓的擴充套件程式,選中壓縮包解壓後的目錄,最後點選執行即可。
img
其中出參註釋、及介面說明,寫在tests裡:
/*這裡是介面說明,和每個出參、入參的意思。*/
img
介面按模組劃分為資料夾:
img
入參:
img
出參示例:
img
正常請求:
img
開發人員即可直接看到介面示例進行開發,而開發人員開發的時候,自己呼叫一次即可儲存為postman檔案,為了加快上線,我們允許將java中實體類變數定義的程式碼(含註釋)直接複製貼上出來。
js等靜態資源快取問題
從短期角度上講,我的要求是減少js檔案的變更,如果有變更,務必更改版本號。那麼如何減少修改,我們的做法是將一部分js寫在html內,反正前後端分離,大不了重新整理一下cdn的節點快取,畢竟大部分瀏覽器也不會主動快取html檔案(大部分瀏覽器會快取js等檔案)。
統一js請求框架
這裡我們使用angular js的請求框架,因為我們內部對angularjs使用較多,比較熟悉,封裝後的請求,可以自動彈窗錯誤請求,可複寫錯誤回撥。
目前效果
目前,我們客戶端看到介面,大概能說出其意思,也能猜出一連串介面的含義,比如
……/classes
可以看出它是獲取班級列表介面,猜到
……/classes/id get獲取id為id的班級詳情
……/classes/id patch 修改班級資訊
……/classes/id delete 刪除班級資訊
至於入參,patch是post的子集、put=patch、delete無入參。
而入參含義,直接開啟postman可以直接檢視每個欄位的含義,並且,可以實時調取開發環境資料(非開發人員電腦),這裡我們使用了多環境。
前端使用統一封裝後的js請求框架也加快了開發進度,不用造輪子。
開發人員,一般程式碼開發寫好,使用postman自我測試,測試完成後,介面文件也就寫好了。
測試人員想了解介面文件的也可以直接使用postman進行匯入檢視。
至此,我們交流成本下降了一大半,剩下開會的內容就是按ui分解需求或者按ui施工了。
總結
經過一番的折騰,開發進度總算快了點,也一定程度上達到了快速上線專案的效果。關於restful風格api,每個人都有自己的見解,只要內部約定清楚,能儘可能少的減少溝通,我覺得就是好的理解。
至於介面工具,可能很多人會說為什麼不用之前的,我覺得以後還是會用的,最好能做到外掛自動化生成api,但是對java開發註釋要求比較嚴格,隨意慢慢來吧,畢竟後面我們還有很多路要走。