SpringCloud Alibaba微服務實戰十一 – Swagger介面文檔聚合

  • 2020 年 2 月 25 日
  • 筆記

導讀:在SpringCloud體系架構中,我們需要的每個服務都需要對外輸出介面文檔,本篇內容主要是給我們的微服務配上Swagger的介面文檔,並在網關層完成介面聚合。

Swagger2簡介

在當下很多項目都會採用前後端分離的模式,前端和後端的工作由不同的開發人員完成。在這種開發模式下,我們需要維護一份及時更新且完整的Rest API介面文檔。傳統意義上的文檔都是後端人員在開發相關介面後手動更新到介面文檔上,但是這種方式很難保證文檔的及時性,而且由於一些原因後端開發人員可能會忘記更新,這樣就會導致隨著開發的進行介面文檔會失去他本身的參考意義,反而會增加溝通成本。而 Swagger 給我們提供了一個全新的維護 API 文檔的方式,他有以下幾個作用:

  • 只需要後端開發人員給介面加上幾個註解,Swagger就可以根據程式碼自動生成API文檔,節省編寫介面文檔的工作量,而且對介面修改時只需要修改相應的註解,能保證介面的及時性;
  • SwaggerUI展現出來的是一份可互動式的API文檔,我們可以直接在介面頁面對功能測試,省去了大量介面聯調的時間;

Swagger2有以下幾個常見註解,大家在使用過程中會經常用到。

@Api

此註解可以用來標記 Controller 的功能,如:

@Api(tags = "product模組")  public class ProductController implements ProductFeign {    }  

@ApiOperation

此註解用來標記一個方法的作用

@ApiOperation(value = "根據產品編碼查找對應的產品")  public ResultData<ProductDTO> getByCode(@PathVariable String productCode){  	...  }  

@ApilmplicitParam@ApilmplicitParams

這組註解用來對參數進行描述,@ApilmplicitParam 有幾個重要的參數:name:參數名value:參數的漢字說明、解釋required:參數是否必須傳paramType:參數放在哪個地方(header:對應@RequestHeader的參數;query :對應@RequestParam的參數;path :對應@PathVariable的參數;body:對應 @RequestBody 的參數;form(普通表單提交) )

@ApiImplicitParam(name = "productCode",value = "產品編碼", required = true,paramType = "path")  public ResultData<ProductDTO> getByCode(@PathVariable String productCode){  	...  }  @ApiImplicitParam(name = "productCode" , value = "產品編碼",required = true, paramType = "query")  public ResultData<String> delete(@RequestParam String productCode){  	...  }  

如果有多個參數,則需要使用@ApilmplicitParams 註解。

@ApiModel@ApiModelProperty

如果參數是一個對象,則需要在對象所在的類上加上此註解。這種一般用在post創建的時候,使用 @RequestBody 這樣的場景,請求參數無法使用 @ApiImplicitParam 註解進行描述的時候 。在具體欄位上則使用@ApiModelProperty註解。如:

@Data  @ApiModel(value = "產品封裝類ProductDTO",description = "產品相關資訊封裝,用於介面傳參")  public class ProductDTO {      @ApiModelProperty(value = "產品主鍵")      private Integer id;      @ApiModelProperty(value = "產品編碼")      private String productCode;      @ApiModelProperty(value = "產品名稱")      private String productName;      @ApiModelProperty(value = "數量")      private Integer count;      @ApiModelProperty(value = "單價")      private BigDecimal price;  }  

使用

在項目中要使用Swagger2很簡單,按照以下幾步即可:

  • 在pom文件中引入jar包 每個微服務都需要使用,我們直接將其引入在cloud-common服務中
<!--swagger2-->  <dependency>  	<groupId>io.springfox</groupId>  	<artifactId>springfox-swagger2</artifactId>      <version>2.9.2</version>  </dependency>    <dependency>  	<groupId>io.springfox</groupId>  	<artifactId>springfox-swagger-ui</artifactId>      <version>2.9.2</version>  </dependency>  
  • 在微服務中編寫swagger2的配置類SwaggerConfig
@Configuration  @EnableSwagger2  public class SwaggerConfig {      private static final String VERSION = "1.0.0";      /**       * 創建API       */      @Bean      public Docket createRestApi(){          return new Docket(DocumentationType.SWAGGER_2)                  .apiInfo(apiInfo())                  .select()                  //指定介麵包所在路徑                  .apis(RequestHandlerSelectors.basePackage("com.javadaily.product.controller"))                  .paths(PathSelectors.any())                  .build();      }        /**       * 添加摘要資訊       */      private ApiInfo apiInfo() {          return new ApiInfoBuilder()                  .title("product-server介面文檔")                  .contact(new Contact("JAVA日知錄","http://javadaily.cn","[email protected]"))                  .description("product-server介面文檔")                  .termsOfServiceUrl("http://javadaily.cn")                  .license("The Apache License, Version 2.0")                  .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")                  .version(VERSION)                  .build();      }  }  

.apis方法用於指定生成註解的範圍,有以下四種取值邏輯RequestHandlerSelectors.any(),為所有介面都生成API文檔,這種方式不必在介面上加任何註解,但是生成的文檔沒有任何注釋,可讀性不高; ②RequestHandlerSelectors.basePackage(xx.xx),為指定包下的controller生成介面文檔 ③RequestHandlerSelectors.withClassAnnotation(Api.class),為有@api註解的介面生成api文檔 ④RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class),為有@ApiOperation註解的方法生成API文檔。

  • 給相關介面加上Swagger的註解 註解的詳細說明參見上文。
  • 打開swagger2介面API頁面http://localhost:8020/swagger-ui.html

網關聚合

我們已經給各個微服務加上了api文檔,但是每次都需要分別輸入各個微服務的文檔地址,不太方便。本節內容主要是將各個微服務的api地址聚合到網關層,打開網關的api地址即可查看其它所有服務的介面文檔,效果如下:

實現步驟如下:

  • 編寫配置類實現 SwaggerResourcesProvider 介面聚合其他微服務的api資源
@Component  @AllArgsConstructor  public class CustomSwaggerResourceProvider implements SwaggerResourcesProvider {      /**       * Swagger2默認的url後綴       */      public static final String SWAGGER2URL = "/v2/api-docs";      /**       * 網關路由       */      private final RouteLocator routeLocator;      private final GatewayProperties gatewayProperties;      /**       * 聚合其他服務介面       * @return       */      @Override      public List<SwaggerResource> get() {          List<SwaggerResource> resourceList = new ArrayList<>();          List<String> routes = new ArrayList<>();          //獲取網關中配置的route          routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));          gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))                  .forEach(routeDefinition -> routeDefinition.getPredicates().stream()                  .filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))                  .forEach(predicateDefinition -> resourceList.add(                              swaggerResource(                                  routeDefinition.getId(),                                  predicateDefinition                                          .getArgs()                                          .get(NameUtils.GENERATED_NAME_PREFIX + "0")                                          .replace("/**",SWAGGER2URL)  //這裡拼接時需要注意  //網關配置account映射到account-service,要麼將網關的配置修改成account-service映射成account-service  //要麼就在這個拼接處解決                          )                  )));          return resourceList;      }        private SwaggerResource swaggerResource(String name, String location) {          SwaggerResource swaggerResource = new SwaggerResource();          swaggerResource.setName(name);          swaggerResource.setLocation(location);          swaggerResource.setSwaggerVersion("2.0");          return swaggerResource;      }  }  
  • 自定義Rest介面
@RestController  @RequestMapping("/swagger-resources")  public class SwaggerHandler {      @Autowired(required = false)      private SecurityConfiguration securityConfiguration;        @Autowired(required = false)      private UiConfiguration uiConfiguration;        private final SwaggerResourcesProvider swaggerResources;        @Autowired      public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {          this.swaggerResources = swaggerResources;      }        @GetMapping("/configuration/security")      public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {          return Mono.just(new ResponseEntity<>(                  Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));      }        @GetMapping("/configuration/ui")      public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {          return Mono.just(new ResponseEntity<>(                  Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()),HttpStatus.OK));      }        @GetMapping("")      public Mono<ResponseEntity> swaggerResources() {          return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));      }  }

好了,各位朋友們,本期的內容到此就全部結束啦,能看到這裡的同學都是優秀的同學,下一個升職加薪的就是你了! 如果覺得這篇文章對你有所幫助的話請掃描下面二維碼加個關注。"轉發" 加 "在看",養成好習慣!咱們下期再見!