springboot實戰之swagger整合
- 2019 年 11 月 7 日
- 筆記
什麼是swagger
Swagger 是一個規範和完整的框架,用於生成、描述、調用和可視化 RESTful 風格的 Web 服務。總體目標是使客戶端和文件系統作為伺服器以同樣的速度來更新。文件的方法,參數和模型緊密集成到伺服器端的程式碼,允許API來始終保持同步。
swagger的核心功能
- 介面的文檔在線自動生成。
- 功能測試。
swagger常用註解說明
常用註解推薦查看如下鏈接,這邊就不再論述
https://my.oschina.net/Rayn/blog/3064162
springboot與swagger整合
這邊整合的內容有如下
- 介面文檔生成與測試
- swagger分組
- 全局token以及局部token配置
- 與介面版本號進行集成
介面文檔在線生成與測試
1、pom.xml引入
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> <exclusions> <exclusion> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> </exclusion> <exclusion> <groupId>io.swagger</groupId> <artifactId>swagger-models</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.5.21</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-models</artifactId> <version>1.5.21</version> </dependency>
ps:排除2.9.2版本的swagger-models和swagger-annotations,而引入1.5.21版本,是因為使用2.9.2版本時,當欄位為數值型映射文檔欄位屬性會出異常
2、創建Swagger的配置類
@Configuration public class SwaggerConfig { private ApiInfo apiInfo(){ return new ApiInfoBuilder() .version("1.0") .title("API介面文檔 ") .description("API介面文檔") .build(); } @Bean public Docket createRestApi(){ return new Docket(DocumentationType.SWAGGER_2).useDefaultResponseMessages(false) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.github.lybgeek.swagger")) .paths(PathSelectors.regex("/api.*")).build(); } }3、在啟動類上加上@EnableSwagger2註解
@SpringBootApplication @EnableSwagger2 public class SwaggerSourceApplication { public static void main( String[] args ) { SpringApplication.run(SwaggerSourceApplication.class,args); } }4、在實體bean上加上@ApiModel註解,實體的欄位上加上@ApiModelProperty註解
@Data @Accessors(chain = true) @AllArgsConstructor @NoArgsConstructor @Builder @ApiModel public class BookDTO { @ApiModelProperty(value = "編號", name = "id", example = "1") private Long id; @ApiModelProperty(value = "書名", name = "name", example = "swagger2入門") @NotNull(message = "書名不能為空") private String bookName; @ApiModelProperty(value = "作者", name = "author", example = "張三") @NotNull(message = "作者不能為空") private String author; @ApiModelProperty(value = "描述", name = "description", example = "swagger2入門實戰") private String description; @ApiModelProperty(value = "價格", name = "price", example = "10") @NotNull(message = "價格不能為空") private BigDecimal price; @ApiModelProperty(value = "庫存", name = "stock", example = "20") @NotNull(message = "庫存不能為空") private Integer stock; }5、contoller層做形如下配置
@RestController @RequestMapping(value={"/api/{version}/book","/book"}) @Slf4j @Api(value = "書目管理", tags = {"書目管理"}) @ApiResponses({@ApiResponse(code = 200, message = "成功")}) public class BookController { @Autowired private BookService bookService; @PostMapping(value="/add") @ApiOperation(value = "添加書籍", notes = "添加書籍") public Result<BookDTO> addBook(@Valid BookDTO bookDTO, BindingResult bindingResult){ Result<BookDTO> result = new Result<>(); if (bindingResult.hasErrors()){ return ResultUtil.INSTANCE.getFailResult(bindingResult, result); } try { BookDTO book = bookService.addBook(bookDTO); result.setData(book); } catch (Exception e) { log.error("addBook error:"+e.getMessage(),e); result.setStatus(Result.fail); result.setMessage(e.getMessage()); } return result; } @PostMapping(value="/update") @ApiOperation(value = "更新書籍", notes = "更新書籍") public Result<BookDTO> upadteBook(BookDTO bookDTO){ Result<BookDTO> result = new Result<>(); if(bookDTO.getId() == null){ result.setStatus(Result.fail); result.setMessage("id不能為空"); return result; } BookDTO book = bookService.editBook(bookDTO); result.setData(book); return result; } @PostMapping(value="/del/{id}") @ApiOperation(value = "刪除書籍", notes = "根據書籍編號刪除書籍") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "書籍編號", dataType = "Long", required = true) }) public Result<Boolean> delBook(@PathVariable("id")Long id){ Result<Boolean> result = new Result<>(); Boolean delFlag = bookService.delBookById(id); result.setData(delFlag); return result; } @PostMapping(value="/list") @ApiOperation(value = "查找書籍列表", notes = "根據查詢條件查詢書籍") public Result<List<BookDTO>> listUsers(BookDTO bookDTO){ Result<List<BookDTO>> result = new Result<>(); List<BookDTO> books = bookService.listBooks(bookDTO); result.setData(books); return result; } @PostMapping(value="/page") @ApiOperation(value = "書籍分頁", notes = "根據查詢條件查詢書籍") @ApiImplicitParams({ @ApiImplicitParam(name = "pageNo", value = "頁碼", dataType = "Integer", required = true), @ApiImplicitParam(name = "pageSize", value = "分頁大小", dataType = "Integer", required = true) }) public Result<PageResult<BookDTO>> pageUsers(BookDTO bookDTO,Integer pageNo,Integer pageSize){ PageQuery<BookDTO> pageQuery = new PageQuery<>(); pageQuery.setPageNo(pageNo); pageQuery.setPageSize(pageSize); pageQuery.setQueryParams(bookDTO); Result<PageResult<BookDTO>> result = new Result<>(); PageResult<BookDTO> books = bookService.pageBook(pageQuery); result.setData(books); return result; } @PostMapping(value="/get/{id}") @ApiOperation(value = "查找書籍", notes = "根據書籍編號查找書籍") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "書籍編號", dataType = "Long", required = true) }) public Result<BookDTO> getBookById(@PathVariable("id") Long id){ Result<BookDTO> result = new Result<>(); BookDTO book = bookService.getBookById(id); result.setData(book); return result; } }6、如果程式碼有配置攔截器,可以看具體需求看是否要攔截swagger相關頁面,下面程式碼為不攔截swagger相關頁面配置
@Configuration public class IntercepterConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**") .addResourceLocations("classpath:/static/"); } @Bean public AuthIntercepors authIntercepors() { return new AuthIntercepors(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authIntercepors()).addPathPatterns("/**") .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**","/swagger-ui.html","/doc.html","/docs.html"); } }7、通過訪問項目地址/swagger-ui.html,進入在線介面文檔頁面

swagger分組
swagger 分組只需在swagger配置加入groupName屬性就行了,配置如下
@Configuration public class SwaggerConfig { private ApiInfo apiInfo(){ return new ApiInfoBuilder() .version("1.0") .title("API介面文檔 ") .description("API介面文檔") .build(); } @Bean public Docket createRestApiWithApiVersion(){ return new Docket(DocumentationType.SWAGGER_2).useDefaultResponseMessages(false) .apiInfo(apiInfo()) .groupName(ApiVersionUtil.DEFAULT_API_VERSION) .select() .apis(RequestHandlerSelectors.basePackage("com.github.lybgeek.swagger")) // .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) // .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.regex("/api.*")).build() } @Bean public Docket createRestApi(){ return new Docket(DocumentationType.SWAGGER_2).useDefaultResponseMessages(false) .apiInfo(apiInfo()) .groupName("default") .select() .apis(RequestHandlerSelectors.basePackage("com.github.lybgeek.swagger")) .paths(PathSelectors.regex("(?!/api).+")).build(); } }
swagger設置局部token以及全局token
1、配置局部token
主要是在swagger配置中加入globalOperationParameters屬性就行,其配置如下
@Configuration public class SwaggerConfig { private ApiInfo apiInfo(){ return new ApiInfoBuilder() .version("1.0") .title("API介面文檔 ") .description("API介面文檔") .build(); } @Bean public Docket createRestApiWithApiVersion(){ List<Parameter> pars = new ArrayList<>(); ParameterBuilder tokenPar = new ParameterBuilder(); tokenPar.name(Constant.ACCESS_TOKEN).description(Constant.ACCESS_TOKEN).modelRef(new ModelRef("string")).parameterType("header").required(true).build(); pars.add(tokenPar.build()); return new Docket(DocumentationType.SWAGGER_2).useDefaultResponseMessages(false) .apiInfo(apiInfo()) .groupName(ApiVersionUtil.DEFAULT_API_VERSION) .select() .apis(RequestHandlerSelectors.basePackage("com.github.lybgeek.swagger")) // .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) // .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.regex("/api.*")).build() .genericModelSubstitutes(ResponseEntity.class) //每個介面單獨傳token .globalOperationParameters(pars); }
2、配置全局token
主要是在swagger配置中加入securitySchemes屬性以及securityContexts屬性,其配置如下
@Configuration public class SwaggerConfig { private ApiInfo apiInfo(){ return new ApiInfoBuilder() .version("1.0") .title("API介面文檔 ") .description("API介面文檔") .build(); } @Bean public Docket createRestApi(){ ParameterBuilder tokenPar = new ParameterBuilder(); List<Parameter> pars = new ArrayList<>(); tokenPar.name(Constant.ACCESS_TOKEN).description(Constant.ACCESS_TOKEN).modelRef(new ModelRef("string")).parameterType("header").required(true).build(); pars.add(tokenPar.build()); return new Docket(DocumentationType.SWAGGER_2).useDefaultResponseMessages(false) .apiInfo(apiInfo()) .groupName("default") .select() .apis(RequestHandlerSelectors.basePackage("com.github.lybgeek.swagger")) // .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) // .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // .paths(PathSelectors.regex("/api.*")).build() .paths(PathSelectors.regex("(?!/api).+")).build() .genericModelSubstitutes(ResponseEntity.class) // .globalOperationParameters(pars); //設置全局token .securitySchemes(securitySchemes()) .securityContexts(securityContexts()); } private List<SecurityReference> defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; return Collections.singletonList(SecurityReference.builder() .reference(Constant.ACCESS_TOKEN) .scopes(authorizationScopes).build()); } private List<SecurityContext> securityContexts() { List<SecurityContext> securityContexts=new ArrayList<>(); securityContexts.add( SecurityContext.builder() .securityReferences(defaultAuth()) .forPaths(PathSelectors.regex("(?!/api).+")) .build()); return securityContexts; } private List<ApiKey> securitySchemes() { return newArrayList( new ApiKey(Constant.ACCESS_TOKEN, Constant.ACCESS_TOKEN, "header") ) }

swagger與介面版本號集成
1、介面版本號控制實現
因為這個介面版本號控制的實現,與本文的論述的關聯不大,因此就不展開描述了。感興趣的朋友,可以查看如下鏈接
https://www.jianshu.com/p/fd4a61c47b86
這篇文章講述比較詳細,但是裡面有個實現細節有bug,如果大家是參考這篇文章進行實現就會發現。其bug的解決方案可參考如下鏈接
https://my.oschina.net/pwh19920920/blog/2413927
2、swagger與介面版本號集成
主要依然是在swagger配置中實現,其設置和局部token的設置方式一樣,只是token是設置在header,版本號設置在path,其程式碼如下
@Configuration public class SwaggerConfig { private ApiInfo apiInfo(){ return new ApiInfoBuilder() .version("1.0") .title("API介面文檔 ") .description("API介面文檔") .build(); } @Bean public Docket createRestApiWithApiVersion(){ List<Parameter> pars = new ArrayList<>(); ParameterBuilder tokenPar = new ParameterBuilder(); tokenPar.name(Constant.ACCESS_TOKEN).description(Constant.ACCESS_TOKEN).modelRef(new ModelRef("string")).parameterType("header").required(true).build(); ParameterBuilder versionPar = new ParameterBuilder(); versionPar.name("version").description("version").modelRef(new ModelRef("string")).parameterType("path") .defaultValue(ApiVersionUtil.DEFAULT_API_VERSION).required(false).build(); pars.add(tokenPar.build()); pars.add(versionPar.build()); return new Docket(DocumentationType.SWAGGER_2).useDefaultResponseMessages(false) .apiInfo(apiInfo()) .groupName(ApiVersionUtil.DEFAULT_API_VERSION) .select() .apis(RequestHandlerSelectors.basePackage("com.github.lybgeek.swagger")) // .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) // .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.regex("/api.*")).build() .genericModelSubstitutes(ResponseEntity.class) //每個介面單獨傳token .globalOperationParameters(pars); }
配置比較符合國人使用習慣的UI
1、pom.xml配置
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.3</version> </dependency>
2、在swagger的配置類上,加上@EnableSwaggerBootstrapUI註解
@Configuration @EnableSwaggerBootstrapUI public class SwaggerConfig {3、如果有配置攔截器,要放行doc.html
4、訪問項目地址/doc.html

總結
本文介紹的內容大體都是平常使用swagger會經常碰到的,至於自定義介面文檔排序等優先順序不是那麼高的就不再介紹。更多相關swagger的介紹,大家感興趣可以查看
https://github.com/swagger-api/swagger-ui
或者訪問官網
http://swagger.io
demo鏈接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-swagger
