團隊內部RestAPI開發采用設計驅動開發的模式,即使用API設計文檔解耦前端和后端的開發過程,雙方只在聯調與測試時耦合。在實際開發和與前端合作的過程中,受限于眾多因素的影響,開發效率還有進一步提高的空間。本文的目的是優化工具鏈支持,減少一部分重復和枯燥的勞動。
現狀梳理
前后端工作流
- 需求理解:前后端先理解產品思路、需求的詳細內容
- 敲定接口:后端出API設計文檔初稿,與前端面對面或者在線討論修正,接著后端(有時是前端)把API描述記錄到公司內部的API文檔庫(在線markdown編輯器,提供分級目錄的存儲功能,對如何描述API沒有一定的標準,因此描述格式不統一,因人而異1)。接著根據雙方工作的安排,約定聯調時間
- 獨立開發:雙方獨立開發(也有可能非完全獨立開發,如需要對方的環境配合等;或者存在返工,如API設計發生變更等)
- 系統聯調:測試API基本功能和雙方系統的連通性
- 測試回歸:開發或者QA編寫測試用例并測試業務流程
可優化方向
1. 減少文檔編寫時間
根據個人的開發經驗,后端編寫API設計文檔時常見的情況有:如果是簡單的需求,API數量較少,后端直接通過內部即時通信軟件和前端溝通;如果是復雜的需求,API數量較多,后端會先把API描述寫到本地臨時文檔(純文本、markdown、evernote等)或者內網(內部個人Wiki、git倉庫)中,然后把鏈接發給前端review或者直接面對面溝通。這樣的方式靈活,但存在一些問題,比如:
-
描述格式沒有標準。對于簡單的描述,文檔格式比較隨意,雙方基于約定和經驗理解和開發1;完備的描述,編寫文檔所需時間較長,并且細節復雜(需要考慮不同的HTTP請求類型、HTTP頭部信息、HTTP請求內容等),高質量地創建這份文檔本身就是件非常吃力的事,下游的抱怨聲不絕于耳。當然在合作開發中,文檔越完備,雙方的理解偏差就越少、開發產生的bug就越少,后期也更容易維護代碼、適應人員變更,但是編寫完備的文檔所需要的額外時間也不容忽視,沒有代碼產出的設計文檔可能不得已讓位于現實中整體開發時間的緊張。
以開發“獲得管理員賬戶下可用商戶”為例。如果是簡單的描述,后端告知前端url為
{host}/ajax/shop
,返回的結構是[{"shopId":int,"shopName":string}]
,有經驗的前端會自動判斷出Method
為Get
,Content-type
為application/json
,request不需要附帶參數,不需要對錯誤值做特殊處理;而如果是復雜的描述,后端一般會列出API名稱、功能描述、調用方式、請求參數、請求示例、返回值、成功的返回結果示例、失敗的返回結果示例中的幾項,填充到已有的API模板中2。 -
輸入效率不高。由于開發的API模板缺乏固定的標準,因此只能在例如Wiki、純文本編輯器、markdown編輯器中編寫,無法得到現代IDE中語法高亮、自動補全、錯誤提示等特性的支持,整體感覺就像是在記事本中寫Java。
- 設計文檔中會規定API輸出的數據結構(一般為json數組或者json對象),如果數據結構較為復雜(比如包含有幾十個字段的POJO),要在設計文檔中書寫可讀性良好的數據結構需要更多的時間;如果數據結構中字段缺失或者可讀性差,則會影響前端的文檔理解和代碼開發。
- 如果后端能提供樣例數據自然是最好的,因為后端最熟悉業務邏輯,產生的樣例數據比前端自己Mock的數據更好。但是復雜數據結構的樣例數據的編寫同樣很花時間。
> 舉例:需求要求開發一個新增優惠券API,其樣例數據只能由開發手動生成。如果是修改已有的API,要補充新的樣例數據,開發一般會登錄商戶平臺,打開優惠券頁面,在Chrome中實際操作一遍,抓包得到request的body(json格式),在json格式化網站(如[json.cn](json.cn))美化后復制到API設計文檔中。
-
重復錄入。因為文檔庫功能羸弱,使用不便,所以開發一般先按自己的格式寫一份文檔,但是如果不直接把API錄入到公司文檔庫,則開發需要對一份API出兩份設計文檔。開發一般會開兩個窗口,左邊是API設計文檔的完成件,右邊是公司API文檔庫編輯頁面,然后把左邊格式各異的API描述文本轉換到右邊統一的markdown格式。
例如:想象一下從Wiki文檔的表格中一個個復制粘貼,再編輯成markdown格式文本是典型的成本大于收益的工作。
-
文檔維護成本大。由于文檔和代碼分開存放,由于需要手動操作,因此文檔與代碼同步成本較高。隨著時間推移,不斷修改接口實現的時候都必須同步修改接口文檔,而文檔與代碼又處于兩個不同的媒介,除非有嚴格的管理機制,不然很容易導致不一致現象,并在業務整體交接、開發成員替換時使后來人付出較大的時間成本。
不同的存放形式的優缺點見仁見智,類似于Spring也有XML和JavaConfig兩種配置方式。
2. 減少聯調時間
缺少樣例數據。由于團隊內部前端一般不會全面的了解業務,后端提供的樣例數據往往比前端自己生成的Mock數據對業務需求的把握更準確。如果后端能在API設計文檔中提供樣例數據,一是如果前端沒有自動Mock工具的話,能節約前端生成Mock數據的時間;二是能在聯調前為前端提前發現一些低級錯誤(比如具有業務特征的一些默認值處理、空值處理、字段缺失等場景)。
3. 減少部署時間
beta環境綁定了唯一的beta域名,因此在多分支并行開發時是稀缺資源,較大的項目在beta環境編譯和部署往往消耗很多等待和解決沖突的時間。如果在聯調中發現的問題較多,就需要多次部署beta環境,時間成本十分可觀。
如何減少部署時間另外行文
尋找技術候選
總結起來,上面列出的問題大部分是由于API描述標準不統一引起的,因此要用標準化的工廠代替散亂的手工生產。雖然平時開發的API具有Rest風格、對外網開放,只被企業自己的應用調用,不過普遍的WebAPI開發流程還是適用的。我在網上搜索一些功能較為符合的RestAPI設計工具,將其大致分為3類討論。
第一類:Swagger、Apiary、RAML
人和機器可讀的API描述標準,圍繞該語言有完善的工具鏈:一般有設計、編譯(即Codegen)、測試(有MockServer、自動Mock、本地直連等形式)、文檔(包括靜態文檔,如html和pdf;還有可交互文檔html+js)、合作(多人+多角色合作開發)這幾個模塊,各個標準都差不多。
較為學術性的表述:雖然Web API的實現正變得越來越普及,但在工具方面還缺乏一些被廣泛接受的標準,用以描述、發現,并且理解大量基于API的服務的意義。Web API之“元語言”有三個關鍵領域:API描述、API發現以及API檔案。所謂的API描述,指的是以一種讓人類與機器都可讀的形式對API進行描述,包括API的實現細節,例如資源與URL、表述格式(HTML、XML、JSON等等)、狀態碼以及輸入參數。
Swagger、Apiary、RAML的格式各自采取了一種略有不同的設計方式,但在本質上都提供了相同的基本特性:以多種不同級別的細節對Web API進行描述。
以Swagger23為例,分為5個部分(示例圖來自于RAML,不過功能都差不多)。
- Design:其標準為
OpenAPI
(前身是Swagger API Spec
),提供強大的在線編輯功能,包括語法高亮、錯誤提示、自動完成、實時預覽4,并且支持用戶以Json、Yaml格式撰寫5、導入、導出、轉換文檔。 - Build:設計文檔可以編譯成客戶端和服務端,支持的語言包括Java、NodeJS、C++等主流語言。其中Java服務器端使用流行的
Spring Boot
構建,生成的代碼包括定義的API接口、空實現方法的樣板代碼、業務POJO、配套的Swagger注解。值得注意的是,由自動生成的Swagger注解,可以反向生成最初的API設計文檔 - Test:可在本地服務器運行時使用本地測試功能;用戶也可以使用
SwaggerHub
中提供收費的在線測試功能,主要有MockServer(Auto Mocking
)、問題跟蹤(Issue Tracking
) - Document:可以在線或離線(包括代碼編譯時和運行時)地生成靜態html、pdf等文檔;
SwaggerHub
可以配合API版本,自動同步相應文檔的版本 - Share:
SwaggerHub
提供團隊管理、聯調開發、文檔標注等多人合作開發的支持
再提一下Apiary和RAML。Apiary6使用API Blueprint
標準,Apiary網站提供了在線編輯、實時預覽、Mock、可交互文檔、團隊合作、Github同步、流量追蹤等包含整個API生命周期的所有服務,當然這是收費產品,而且價格不菲;另外,用戶也可以通過開源的命令行工具進行離線的API設計、文檔生成、發布過程,并將其集成到自己的工作流中,這也是它的一大特點。RAML使用RAML1.0
標準,沒有自己的可視化在線開發平臺,而是用官方或第三方的離線工具(如API Workbench
系列)來代替,因此它也存在一些缺點,比如:工具更新不及時,某些Tool不支持最新的RAML1.0
。
第二類:Apidocjs
類似于Intellij Idea的生成JavaDoc
功能,是一種注釋解析器,從C++、Java、Python代碼注釋中基于特定的關鍵字(如@param
、@return
)生成API靜態文檔。由于更像是先代碼實現后生成API文檔,所以不能算作是設計驅動的開發;另外apidocjs也缺乏IDE支持。
第三類:Rap、eolinker
沒有公開的API設計語言,提供在線或離線、閉源或開源的可視化、一體化API開發平臺。這里選擇中文的Rap、eolinker作為代表。Rap是阿里的開源作品,也提供線上服務,核心功能是文檔編輯和自動Mock服務。eolinker是綜合的接口管理平臺,除了常見的功能,還提供接口商店、數據字典等適合創業團隊快速開發API的特性。在此不做進一步介紹。
如何選型?
選型邏輯
- 社區活躍、功能完善,應用成熟。
- 學習成本低、上手時間短。作為業務開發,缺少時間熟悉學習曲線陡峭的知識和工具。
- 功能較多地契合上述優化方向。
- 能補充現有工作流的不足,不做大范圍的代替。
- 要考慮測試環境處于內網造成的障礙。
初步分析
- rap、eolinker、swaggerHub、apiary提供了一整套API開發環境,取代了現有工作流。放棄。
- apidocjs缺乏現代IDE特性支持,輸入效率較低。放棄。
進一步分析
Swagger2 | API Blueprint | RAML | |
---|---|---|---|
Design | 在線編輯、IntelliJ Idea插件 | 在線編輯、命令行、Sublime/Atom/Vim插件 | API Workbench、Sublime/VS插件 |
Design文檔格式 | yaml、json | markdown | yaml |
Build支持 | 在線Build、IntelliJ Idea插件 | / | Maven插件 |
Codegen服務端框架 | Spring Boot | / | JAX—RS |
Test | 運行時手動Mock、第三方工具 | 官方和第三方工具生成MockServer/Client | 第三方工具和在線服務 |
Document | Maven插件生成靜態文檔、在線或運行時生成可交互文檔,支持SpringMVC+注解形式 | 第三方工具 | 第三方工具 |
Share | 在線、收費 | 在線、收費 | 離線、第三方工具 |
綜合考慮,最后選擇Swagger2。因為Swagger對現有的工作流侵入較少;工具較為完整;與團隊使用的Spring MVC
技術棧無縫集成,可以減輕文檔工作量。Swagger2也有一些缺點,如:使用注解方式對代碼有侵入性。
用Swagger2優化現有工作流
-
減少文檔的編寫時間
- 如果后端先編寫獨立的API設計文檔,可利用Swagger在線編輯器或IDE插件的自動完成等特性;yaml格式統一、簡單易懂、表達能力強,較markdown冗余字符更少。通過模仿官方Example很容易學習
OpenAPI
規定的關鍵字。 - 另外后端也可以把API設計文檔直接通過注解的形式,標注在
Controller
類和相關方法上(以Spring MVC
和Spring Boot
為例),即可以通過Java反射在Maven Complie
或運行時生成API設計文檔。Swagger有Intellij Idea
的插件支持,Swagger注解則能利用現代Java IDE的特性,提高輸入效率;另外完善的注解也方便其他開發人員進行后期維護,不需要在設計文檔和代碼實現中來回切換查看。此種方式相當于面向規約的開發模式,即先規定接口,再填充實現。
- 如果后端先編寫獨立的API設計文檔,可利用Swagger在線編輯器或IDE插件的自動完成等特性;yaml格式統一、簡單易懂、表達能力強,較markdown冗余字符更少。通過模仿官方Example很容易學習
- 減少文檔的轉換時間:利用第三方工具實現從Swagger、API Blueprint、RAML格式的互相轉換,或者直接輸出為html靜態文檔,方便整合到現在的工作流中。比如:
API Blueprint
的markdown格式可以存儲到公司的API文檔庫,html靜態文檔可以存儲到內部Wiki。 - 減少(可能的)開發時間:如果已有獨立的API設計文檔,在Swagger Editor中生成基于
Maven + Spring Boot
的服務端代碼,不過生成的POJO和Controller類的命名可能不太理想,需要自己調整。 - 減少聯調時間:后端可以在設計文檔或注解中指定API或者POJO的Example數據,節約前端手動編寫Mock數據的時間。
附錄1:流程實例演示(腳手架為Spring MVC
)
1. 標注相應的Swagger注解作為API設計文檔
先建立RestController類、相應的API空方法、POJO作為骨架。對應的API設計文檔見文末的Reference
節。
@Api("Users")
@RestController
@RequestMapping(value = "/users")
public class UserController {@ApiOperation(value = "創建用戶", notes = "根據User對象創建用戶")@PostMappingpublic String postUser(@RequestBody User user) {return null;}@ApiOperation(value = "獲取用戶詳細信息", notes = "根據url的id來獲取用戶詳細信息")@GetMapping("/{id}")public User getUser(@PathVariable Long id) {return null;}
}
class User{private Long id;private String name;private String age;//getter,setter
}
2. 生成API設計文檔
生成的具體方式按照耗時長短排列為:Maven Complie
、Test Case、Server Runtime。可在Swagger Editor中預覽相應的可交互文檔。根據前端的反饋,修改Swagger注解,并把新的文檔存儲到內部Wiki或者API文檔庫(如果改動量大的話,利用Diff工具提高效率)。
3. 在Swagger-UI提供的可視化頁面中完成自測
開發完成后啟動Server,Swagger-UI的訪問地址為http://localhost:8080/swagger-ui.html
4. 與前端聯調
為了減少beta環境的沖突、加快部署速度,最好在本地開發環境聯調。
附錄2:Swagger配置與使用
【5分鐘指南】Swagger2環境配置與使用
附錄3:YAML格式的API描述文檔示例
swagger: '2.0'
info:description: Click Link Below for Helpversion: v1title: demo13termsOfService: 'http://www.github.com/kongchen/swagger-maven-plugin'
host: HOST
basePath: /s
tags:- name: Users
schemes:- http
paths:/users:post:tags:- Userssummary: 創建用戶description: 根據User對象創建用戶operationId: postUserparameters:- in: bodyname: bodyrequired: falseschema:$ref: '#/definitions/User'responses:'200':description: successful operationschema:type: string'/users/{id}':get:tags:- Userssummary: 獲取用戶詳細信息description: 根據url的id來獲取用戶詳細信息operationId: getUserparameters:- name: 'id'in: pathrequired: truetype: integerformat: int64responses:'200':description: successful operationschema:$ref: '#/definitions/User'
definitions:User:type: objectproperties:id:type: integerformat: int64name:type: stringage:type: string
Reference
- Swagger:Rest API的描述語言
- RAML vs. Swagger vs. API Blueprint
- Springfox Reference Documentation
- Swagger使用
- swagger-maven-plugin
- 通過Swagger進行API設計,與Tony Tam的一次對話
- API 設計: RAML、Swagger、Blueprint三者的比較
- API描述、發現與檔案入門
- Spring Boot中使用Swagger2構建強大的RESTful API文檔
- API Design And Documentation
- Swagger與其他API文檔編寫工具對比
- 以“云打印機設置”中的一個API為例,簡單描述的典型。
?
- 自定義API模板。
?
- swagger的Design-Build-Document流程
?
- 實時預覽。
?
- Swagger支持YAML格式。
?
- Apiary的Design-Use-Implement流程。
?