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