swagger搭建
pom
<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>
application.yml
spring:
application:
name: swagger
mvc:
path match:
matching-strategy: ant_path_matcher
bootstrap.yml
server:
port: 8050
CusSwaggerConfig
package com.learn.swagger.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author PC
*/
@Configuration
@EnableSwagger2
public class CusSwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.paths(PathSelectors.any())
.build();
}
/**
* 用于定义API主界面的信息,比如可以声明所有的API的总标题、描述、版本
*
* @return api信息
*/
private ApiInfo apiInfo() {
Contact contact = new Contact(
"wm",
"https://www.***.com",
"***@qq.com");
return new ApiInfoBuilder()
//标题
.title("swagger")
//描述
.description("Swagger文档")
//版本
.version("1.0")
.contact(contact)
.build();
}
}
测试
启动项目,访问http://127.0.0.1:8050/swagger-ui.html
有两个接口需要注意下,打开浏览器控制台
/swagger-resources对应的是下图的结果
/v2/api-docs对应的是文档的结果
这里有个问题,正常的分布式微服务项目可能有很多个服务,每一个微服务对应一个地址使用起来比较麻烦,那么能不能把所有服务的文档在一个swagger-ui中显示呢,答案是肯定的,下面来说明下实现方案
微服务聚合swagger
首先让我们定位下上图中的两个接口
- /swagger-resources位于springfox.documentation.swagger.web.ApiResourceController#swaggerResources,该接口是获取服务列表的,微服务聚合可以从这里下手。查看swaggerResources的代码
private final SwaggerResourcesProvider swaggerResources;
@RequestMapping
@ResponseBody
public ResponseEntity<List<SwaggerResource>> swaggerResources() {
return new ResponseEntity(this.swaggerResources.get(), HttpStatus.OK);
}
发现其调用了SwaggerResourcesProvider接口,那么我们就可以自己实现一个接口实现类来实现微服务的聚合。
- /v2/api-docs位于springfox.documentation.swagger2.web.Swagger2Controller#getDocumentation,该接口可以获取服务的文档,如无特殊需求可以不动
代码实现
实现思路其实就是从注册中心获取各个微服务,然后通过网关去访问各个服务的/v2/api-docs接口,通过网关访问可能会有跨域报错,解决方案可参考
同时swagger上可能不想访问到某些服务的文档,像gateway,一般不会有业务接口写到gateway上,因此也需要支持排除功能
CusSwaggerProperties
package com.learn.swagger.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author wm
* swagger配置属性
*/
@Component
@ConfigurationProperties(value = "cus.swagger")
public class CusSwaggerProperties {
/**
* 跳过生成swagger文档
*/
private List<String> skipPath;
private String gatewayRouteName = "gateway";
public List<String> getSkipPath() {
return skipPath;
}
public void setSkipPath(List<String> skipPath) {
this.skipPath = skipPath;
}
public String getGatewayRouteName() {
return gatewayRouteName;
}
public void setGatewayRouteName(String gatewayRouteName) {
this.gatewayRouteName = gatewayRouteName;
}
}
SwaggerServiceImpl
package com.learn.swagger.app.service.impl;
import com.learn.swagger.config.CusSwaggerProperties;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.net.URI;
import java.util.*;
/**
* @author PC
* swagger接口实现类
*/
@Primary
@Service
public class SwaggerServiceImpl implements SwaggerResourcesProvider {
private final static String API_DOCS_URL_FORMAT = "/%s/v2/api-docs";
private final static String SWAGGER_VERSION = "2.0";
private final DiscoveryClient discoveryClient;
@Autowired
public SwaggerServiceImpl(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
private CusSwaggerProperties cusSwaggerProperties;
@Autowired
public void setSwaggerProperties(CusSwaggerProperties cusSwaggerProperties) {
this.cusSwaggerProperties = cusSwaggerProperties;
}
@Override
public List<SwaggerResource> get() {
List<String> instanceIdList = discoveryClient.getServices();
if (CollectionUtils.isEmpty(instanceIdList)) {
return new ArrayList<>();
}
List<SwaggerResource> swaggerResourceList = new ArrayList<>();
for (String instanceId : instanceIdList) {
if (CollectionUtils.containsAny(cusSwaggerProperties.getSkipPath(), instanceId)) {
continue;
}
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(instanceId);
swaggerResource.setSwaggerVersion(SWAGGER_VERSION);
swaggerResource.setUrl(pendingUrl(instanceId));
swaggerResourceList.add(swaggerResource);
}
return swaggerResourceList;
}
private String pendingUrl(String instanceId) {
if (StringUtils.isEmpty(instanceId)) {
return StringUtils.EMPTY;
}
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(cusSwaggerProperties.getGatewayRouteName());
if (CollectionUtils.isEmpty(serviceInstanceList)) {
return StringUtils.EMPTY;
}
URI gatewayUri = serviceInstanceList.get(0).getUri();
if (Objects.isNull(gatewayUri)) {
return StringUtils.EMPTY;
}
return gatewayUri + String.format(API_DOCS_URL_FORMAT, instanceId);
}
}
application.yml
cus:
swagger:
skip-path: gateway
gateway-route-name: gateway
测试
建一个业务服务demo,参考swagger搭建,但是不引入springfox-swagger-ui的pom
在测试之前,还需要在gateway中设置好swagger和demo的路由信息,如下
- gateway项目的application.yml
spring:
application:
name: gateway
cloud:
gateway:
routes:
# 路由id,可以任意设置
- id: demo
# lb: 使用负载均衡策略
uri: lb://demo
# 路由断言
predicates:
- Path=/demo/**
# 路由id,可以任意设置
- id: swagger
# lb: 使用负载均衡策略
uri: lb://swagger
# 路由断言
predicates:
- Path=/swagger/**
启动eureka、gateway、swagger、demo
访问http://127.0.0.1:8080/swagger/swagger-ui.html
点击demo可以切换文档
参考资料
[1].swagger代码
[2].gatway代码
[3].eureka代码
[4].demo代码