Spring Boot給Java開發(fā)人員的生產(chǎn)力帶來(lái)極大的提高,尤其是構(gòu)建RESTful API更是方便箍邮。使用RESTful服務(wù)通常是涉及到多個(gè)終端的團(tuán)隊(duì),比如Android杖剪、iOS菩收、WEB等。為了讓大家溝通順暢鬓椭,通常我們需要編寫一份詳細(xì)的RESTful業(yè)務(wù)接口文檔颠猴,文檔形式有Word或者Excel。但是我們也會(huì)發(fā)現(xiàn)有如下問(wèn)題:
- 接口非常多小染,細(xì)節(jié)又復(fù)雜翘瓮,如果由程序員高質(zhì)量的輸出一個(gè)文檔,經(jīng)常耗時(shí)長(zhǎng)而且效果也不好裤翩,抱怨聲不絕與耳资盅。
- 隨著時(shí)間的推移,文檔需要與代碼保持同步踊赠。但由于大部分程序員都還有著文檔不重要的思想呵扛,于是總有這樣那樣的原因?qū)е鲁绦騿T不愿意或遺忘了更新文檔。
我一直認(rèn)為筐带,代碼就是最好的文檔今穿,如果能將代碼和注釋說(shuō)明很好結(jié)合在一塊。既減輕了研發(fā)人員的負(fù)擔(dān)伦籍,又能輸出高質(zhì)量的文檔蓝晒,那就最好不過(guò)了。這一點(diǎn)Spring Boot也替我們想到了帖鸦,那就是RESTful API的重磅好伙伴 Swagger2芝薇。
具體效果圖如下:
下面我們以Chapter 3-1-1: 構(gòu)建一個(gè)復(fù)雜的RESTful API及單元測(cè)試中的代碼為例,看看如何將Swagger2集成到Spring Boot工程中創(chuàng)建一個(gè)RESTful API文檔
pom.xml中添加Swagger2依賴
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
創(chuàng)建Swagger2配置類
在RestApplication.java同級(jí)路徑下創(chuàng)建SwaggerConfig.java
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("使用Swagger2構(gòu)建RESTful APIs")
.description("客戶端與服務(wù)端接口文檔")
.termsOfServiceUrl("http://localost:8080")
.contact("bluecoffee")
.version("1.0.0")
.build();
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.bluecoffee.rest"))
.paths(PathSelectors.any())
.build();
}
}
上述代碼中富蓄,通過(guò)@Configuration注解剩燥,讓Spring來(lái)加載該類配置,通過(guò)@EnableSwagger2注解來(lái)啟用Swagger2立倍。
再通過(guò)createRestApi函數(shù)創(chuàng)建Docket的Bean之后灭红,apiInfo是用來(lái)創(chuàng)建接口文檔的基本信息(這些基本信息會(huì)展現(xiàn)在文檔頁(yè)面中)。select()函數(shù)返回一個(gè)ApiSelectorBuilder實(shí)例用來(lái)控制哪些接口暴露給Swagger來(lái)展現(xiàn)口注,本例采用指定掃描的包路徑來(lái)定義变擒,Swagger會(huì)掃描該包下所有Controller定義的API,并產(chǎn)生文檔內(nèi)容(除了被@ApiIgnore指定的請(qǐng)求)寝志。
創(chuàng)建API接口描述信息
@RestController
@RequestMapping(value="/books")
public class BookController {
// 創(chuàng)建線程安全的Map
static Map<Long, Book> books = Collections.synchronizedMap(new HashMap<Long, Book>());
@ApiOperation(value="獲取書籍列表", notes="")
@RequestMapping(value={"/"}, method=RequestMethod.GET)
public List<Book> getBookList() {
// 處理"/books/"的GET請(qǐng)求娇斑,用來(lái)獲取圖書列表
// 還可以通過(guò)@RequestParam傳遞參數(shù)來(lái)進(jìn)行查詢條件或者翻頁(yè)信息的傳遞
List<Book> r = new ArrayList<Book>(books.values());
return r;
}
@ApiOperation(value="創(chuàng)建書籍", notes="根據(jù)Book對(duì)象創(chuàng)建書籍")
@ApiImplicitParam(name = "book", value = "書籍實(shí)體對(duì)象Book", required = true, dataType = "Book")
@RequestMapping(value="/", method=RequestMethod.POST,produces = "application/json")
public JsonResult createBook(@RequestBody Book book) {
// 處理"/books/"的POST請(qǐng)求策添,用來(lái)創(chuàng)建User
// 除了@ModelAttribute綁定參數(shù)之外,還可以通過(guò)@RequestParam從頁(yè)面中傳遞參數(shù)
books.put(book.getBookId(), book);
JsonResult jr = new JsonResult();
jr.setIsSuccessful(true);
jr.setResultCode("0000");
jr.setResultDesc("success");
return jr;
}
@ApiOperation(value="獲取書籍詳細(xì)信息", notes="根據(jù)URL中的bookId來(lái)獲取書籍詳細(xì)信息")
@ApiImplicitParam(name = "bookId", value = "書籍ID", required = true, dataType = "Long")
@RequestMapping(value="/{bookId}", method=RequestMethod.GET)
public Book getBook(@PathVariable Long bookId) {
// 處理"/books/{id}"的GET請(qǐng)求毫缆,用來(lái)獲取url中id值的Book信息
// url中的id可通過(guò)@PathVariable綁定到函數(shù)的參數(shù)中
return books.get(bookId);
}
@ApiOperation(value="更新書籍信息", notes="根據(jù)URL中的bookId來(lái)指定更新書籍唯竹,并根據(jù)傳過(guò)來(lái)的Book信息來(lái)更新書籍詳細(xì)信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "bookId", value = "書籍ID", required = true, dataType = "Long"),
@ApiImplicitParam(name = "book", value = "書籍詳細(xì)實(shí)體book", required = true, dataType = "Book")
})
@RequestMapping(value="/{bookId}", method=RequestMethod.PUT)
public JsonResult putBook(@PathVariable Long bookId, @RequestBody Book book) {
// 處理"/books/{bookId}"的PUT請(qǐng)求,用來(lái)更新Book信息
Book b = books.get(bookId);
b.setTitle(book.getTitle());
b.setAuthor(book.getAuthor());
books.put(bookId, b);
JsonResult jr = new JsonResult();
jr.setIsSuccessful(true);
jr.setResultCode("0000");
jr.setResultDesc("success");
return jr;
}
@ApiOperation(value="刪除書籍", notes="根據(jù)URL中的bookId來(lái)指定刪除書籍")
@ApiImplicitParam(name = "bookId", value = "書籍ID", required = true, dataType = "Long")
@RequestMapping(value="/{bookId}", method=RequestMethod.DELETE)
public JsonResult deleteBook(@PathVariable Long bookId) {
// 處理"/books/{bookId}"的DELETE請(qǐng)求苦丁,用來(lái)刪除Book
books.remove(bookId);
JsonResult jr = new JsonResult();
jr.setIsSuccessful(true);
jr.setResultCode("0000");
jr.setResultDesc("success");
return jr;
}
}
我們通過(guò)@ApiOperation注解來(lái)給API增加說(shuō)明浸颓、通過(guò)@ApiImplicitParams、@ApiImplicitParam注解來(lái)給參數(shù)增加說(shuō)明旺拉。
完成這些工作后产上,我們?cè)賳?dòng)Spring Boot程序,訪問(wèn):http://localhost:8080/swagger-ui.html蛾狗。就能看到之前所展示的RESTful API的頁(yè)面晋涣。我們可以再點(diǎn)開具體的API請(qǐng)求,以POST類型的/books/
為例:
API測(cè)試
在上圖中我們可以看到book的Value是一個(gè)輸入框沉桌,下方還有一個(gè)"Try it out!"按鈕谢鹊。沒(méi)錯(cuò),Swagger2還提供了接口測(cè)試功能留凭,我們只要按照Book對(duì)象的Model Schema(黃色區(qū)域)示例進(jìn)行參數(shù)修改撇贺,然后點(diǎn)擊"Try it out!"按鈕就可以進(jìn)行接口測(cè)試了,大家也可以自行測(cè)試一下其他接口冰抢。
小結(jié)
相比編寫Word或Excel文檔而言,Swagger2雖然對(duì)代碼有一定的侵入性艘狭,但是我個(gè)人覺(jué)得還是可以接受的挎扰,畢竟減少了很多工作量,同時(shí)也增加了代碼的可讀性巢音,非常值得推薦遵倦。
對(duì)比Swagger2,我還使用過(guò)apiDoc這個(gè)工具官撼,使用感覺(jué)也不錯(cuò)梧躺,大家有興趣也可以去試試看。