使用swagger作為restful api的doc文檔生成
初衷
記得以前寫接口,寫完後會整理一份API接口文檔,而文檔的格式如果沒有具體要求的話,最終展示的文檔則完全決定於開發者的心情。也許多點,也許少點。甚至,接口總是需要適應新需求的,修改了,增加了,這份文檔維護起來就很困難了。於是發現了swagger,自動生成文檔的工具。
swagger介紹
首先,官網這樣寫的:
Swagger – The World‘s Most Popular Framework for APIs.
因為自強所以自信。swagger官方更新很給力,各種版本的更新都有。swagger會掃描配置的API文檔格式自動生成一份json數據,而swagger官方也提供了ui來做通常的展示,當然也支持自定義ui的。不過對後端開發者來說,能用就可以了,官方就可以了。
最強的是,不僅展示API,而且可以調用訪問,只要輸入參數既可以try it out.
效果為先,最終展示doc界面,也可以設置為中文:
在dropwizard中使用
詳細信息見另一篇在dropwizard中使用Swagger
在spring-boot中使用
以前總是看各種博客來配置,這次也不例外。百度了千篇一律卻又各有細微的差別,甚至時間上、版本上各有不同。最終還是去看官方文檔,終於發現了官方的sample。針對於各種option的操作完全在demo中了,所以clone照抄就可以用了。
github sample源碼
配置
1.需要依賴兩個包:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${springfox-version}</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${springfox-version}</version> </dependency>
第一個是API獲取的包,第二是官方給出的一個ui界面。這個界面可以自定義,默認是官方的,對於安全問題,以及ui路由設置需要著重思考。
2.swagger的configuration
需要特別註意的是swagger scan base package,這是掃描註解的配置,即你的API接口位置。
@Configuration @EnableSwagger2 public class SwaggerConfig { public static final String SWAGGER_SCAN_BASE_PACKAGE = "com.test.web.controllers"; public static final String VERSION = "1.0.0"; ApiInfo apiInfo() { return new ApiInfoBuilder() .title("Swagger API") .description("This is to show api description") .license("Apache 2.0") .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html") .termsOfServiceUrl("") .version(VERSION) .contact(new Contact("","", "[email protected]")) .build(); } @Bean public Docket customImplementation(){ return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage(SWAGGER_SCAN_BASE_PACKAGE)) .build() .directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class) .directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class) .apiInfo(apiInfo()); } }
當然,scan package 也可以換成別的條件,比如:
@Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .build(); }
3.在API上做一些聲明
//本controller的功能描述 @Api(value = "pet", description = "the pet API") public interface PetApi { //option的value的內容是這個method的描述,notes是詳細描述,response是最終返回的json model。其他可以忽略 @ApiOperation(value = "Add a new pet to the store", notes = "", response = Void.class, authorizations = { @Authorization(value = "petstore_auth", scopes = { @AuthorizationScope(scope = "write:pets", description = "modify pets in your account"), @AuthorizationScope(scope = "read:pets", description = "read your pets") }) }, tags={ "pet", }) //這裏是顯示你可能返回的http狀態,以及原因。比如404 not found, 303 see other @ApiResponses(value = { @ApiResponse(code = 405, message = "Invalid input", response = Void.class) }) @RequestMapping(value = "/pet", produces = { "application/xml", "application/json" }, consumes = { "application/json", "application/xml" }, method = RequestMethod.POST) ResponseEntity<Void> addPet( //這裏是針對每個參數的描述 @ApiParam(value = "Pet object that needs to be added to the store" ,required=true ) @RequestBody Pet body); }
案例
package com.test.mybatis.web.controllers; import com.test.mybatis.domain.entity.City; import com.test.mybatis.domain.entity.Hotel; import com.test.mybatis.domain.mapper.CityMapper; import com.test.mybatis.domain.mapper.HotelMapper; import com.test.mybatis.domain.model.common.BaseResponse; import io.swagger.annotations.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; /** * Created by miaorf on 2016/9/10. */ @Api(value = "Test", description = "test the swagger API") @RestController public class TestController { @Autowired private CityMapper cityMapper; @Autowired private HotelMapper hotelMapper; @ApiOperation(value = "get city by state", notes = "Get city by state", response = City.class) @ApiResponses(value = {@ApiResponse(code = 405, message = "Invalid input", response = City.class) }) @RequestMapping(value = "/city", method = RequestMethod.GET) public ResponseEntity<BaseResponse<City>> getCityByState( @ApiParam(value = "The id of the city" ,required=true ) @RequestParam String state){ City city = cityMapper.findByState(state); if (city!=null){ BaseResponse response = new BaseResponse(city,true,null); return new ResponseEntity<>(response, HttpStatus.OK); } return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @ApiOperation(value = "save city", notes = "", response = City.class) @RequestMapping(value = "/city", method = RequestMethod.POST) public ResponseEntity<BaseResponse<City>> saveCity( @ApiParam(value = "The id of the city" ,required=true ) @RequestBody City city){ int save = cityMapper.save(city); if (save>0){ BaseResponse response = new BaseResponse(city,true,null); return new ResponseEntity<>(response, HttpStatus.OK); } return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @ApiOperation(value = "save hotel", notes = "", response = Hotel.class) @RequestMapping(value = "/hotel", method = RequestMethod.POST) public ResponseEntity<BaseResponse<Hotel>> saveHotel( @ApiParam(value = "hotel" ,required=true ) @RequestBody Hotel hotel){ int save = hotelMapper.save(hotel); if (save>0){ BaseResponse response = new BaseResponse(hotel,true,null); return new ResponseEntity<>(response, HttpStatus.OK); } return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @ApiOperation(value = "get the hotel", notes = "get the hotel by the city id", response = Hotel.class) @RequestMapping(value = "/hotel", method = RequestMethod.GET) public ResponseEntity<BaseResponse<Hotel>> getHotel( @ApiParam(value = "the hotel id" ,required=true ) @RequestParam Long cid){ List<Hotel> hotels = hotelMapper.selectByCityId(cid); return new ResponseEntity<>(new BaseResponse(hotels,true,null), HttpStatus.OK); } @ApiOperation(value = "update the hotel", notes = "update the hotel", response = Hotel.class) @RequestMapping(value = "/hotel", method = RequestMethod.PUT) public ResponseEntity<BaseResponse<Hotel>> updateHotel( @ApiParam(value = "the hotel" ,required=true ) @RequestBody Hotel hotel){ int result = hotelMapper.update(hotel); return new ResponseEntity<>(new BaseResponse(result,true,null), HttpStatus.OK); } @ApiOperation(value = "delete the hotel", notes = "delete the hotel by the hotel id", response = City.class) @RequestMapping(value = "/hotel", method = RequestMethod.DELETE) public ResponseEntity<BaseResponse<Hotel>> deleteHotel( @ApiParam(value = "the hotel id" ,required=true ) @RequestParam Long htid){ int result = hotelMapper.delete(htid); return new ResponseEntity<>(new BaseResponse(result,true,null), HttpStatus.OK); } }
4.設定訪問API doc的路由
在配置文件中,application.yml中聲明:
springfox.documentation.swagger.v2.path: /api-docs
這個path就是json的訪問request mapping.可以自定義,防止與自身代碼沖突。
API doc的顯示路由是:http://localhost:8080/swagger-ui.html
如果項目是一個webservice,通常設定home / 指向這裏:
@Controller public class HomeController { @RequestMapping(value = "/swagger") public String index() { System.out.println("swagger-ui.html"); return "redirect:swagger-ui.html"; } }
5.訪問
就是上面的了。但是,註意到安全問題就會感覺困擾。首先,該接口請求有幾個:
http://localhost:8080/swagger-resources/configuration/ui
http://localhost:8080/swagger-resources
http://localhost:8080/api-docs
http://localhost:8080/swagger-resources/configuration/security
除卻自定義的url,還有2個ui顯示的API和一個安全問題的API。關於安全問題的配置還沒去研究,但目前發現一個問題是在我的一個項目中,所有的url必須帶有query htid=xxx,這是為了sso portal驗證的時候需要。這樣這個幾個路由就不符合要求了。
如果不想去研究安全問題怎麽解決,那麽可以自定ui。只需要將ui下面的文件拷貝出來,然後修改請求數據方式即可。
6. 設置在生產環境關閉swagger
具體參考 http://www.cnblogs.com/woshimrf/p/disable-swagger.html
參考:
1.swagger官網:http://swagger.io/
2.github: https://github.com/swagger-api/swagger-codegen/blob/master/samples/server/petstore/springboot/src/main/java/io/swagger/api/PetApi.java
使用swagger作為restful api的doc文檔生成