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

Tags: