導讀:在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","jianzh5@163.com"))
.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)));
}
}
好了,各位朋友們,本期的內容到此就全部結束啦,能看到這里的同學都是優秀的同學,下一個升職加薪的就是你了!
如果覺得這篇文章對你有所幫助的話請掃描下面二維碼加個關注。
“轉發” 加 “在看”,養成好習慣!咱們下期再見!
系列文章
- SpringCloud Alibaba微服務實戰十 - 服務網關
- SpringCloud Alibaba微服務實戰九 - Seata 容器化
- SpringCloud Alibaba微服務實戰八 - Seata 整合Nacos
- SpringCloud Alibaba微服務實戰七 - 分布式事務
- SpringCloud Alibaba微服務實戰六 - 配置隔離
- SpringCloud Alibaba微服務實戰五 - 限流熔斷
- SpringCloud Alibaba微服務實戰四 - 版本管理
- SpringCloud Alibaba微服務實戰三 - 服務調用
- SpringCloud Alibaba微服務實戰二 - 服務注冊
- SpringCloud Alibaba微服務實戰一 - 基礎環境准備