­

springboot实战之swagger整合

  • 2019 年 11 月 7 日
  • 笔记

什么是swagger

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。

swagger的核心功能

  • 接口的文档在线自动生成。
  • 功能测试。

swagger常用注解说明

常用注解推荐查看如下链接,这边就不再论述

https://my.oschina.net/Rayn/blog/3064162

springboot与swagger整合

这边整合的内容有如下

  1. 接口文档生成与测试
  2. swagger分组
  3. 全局token以及局部token配置
  4. 与接口版本号进行集成

接口文档在线生成与测试

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