网关集成swagger
1. 简单原理说明:
打开swagger提供的前端页面“http: //host:port/swagger-ui.html”时,会调用/swagger-resources接口得到spec列表,列表元素结构如下:
[{"name":"接口测试1",
"url":"/v2/api-docs?group=接口测试1",
"swaggerVersion":"2.0",
"location":"/v2/api-docs?group=接口测试1"},
……
]
name是在spec列表中展示的名称,“/v2/api-docs?group=接口测试1”用于在选择spec时,调用“http: //host:port/v2/api-docs?group=接口测试1” 获取到对应spec的swagger接口信息用于展示。
网关本身具有路由的功能,对于连接注册中心的网关,默认使用serverName作为路由的路径,请求http: //host:port/serverName的消息会被分发到名为serverName的服务集群中。
所以只需要修改/swagger-resources接口的返回结果,在url和location前拼接服务名称,即可在获取具体接口信息时,将请求路径变为http: //host:port/serverName/v2/api-docs,该请求在网关处根据serverName被分发到具体的服务上,获取到该服务的swagger接口信息。
2. 修改/swagger-resources返回的结果
swagger的ApiResourceController提供了该接口的调用,在方法中,使用了SwaggerResourcesProvider的实现类来获取List<SwaggerResource>集合,需要重新实现SwaggerResourcesProvider并在注入时优先注入自己的实现。
在网关的pom文件中引入依赖:
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
实现代码如下:
@Component
@Primary
public class GateSwaggerResourceProvider implements SwaggerResourcesProvider {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
RestTemplate restTemplate;
@Autowired
private SpringClientFactory springClientFactory;
@Value("${spring.application.name}")
private String applicationName;
private String uri = "/swagger-resources";
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
// 添加自己以外的其他的服务
discoveryClient.getServices().stream().filter(s -> !s.equals(applicationName)).forEach(name -> {
resources.addAll(getListSwaggerResource(name));
});
return resources;
}
//用于获取服务名称对应的SwaggerResource list
private List<SwaggerResource> getListSwaggerResource(String instantName){
ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(instantName);
Server server = loadBalancer.chooseServer(null);
String url = "http://"+server.getHostPort()+this.uri;
List body=null;
try {
body = restTemplate.getForObject(url, List.class, (Object) null);
}catch (Exception e){
// e.printStackTrace();
}
return this.exchangeListSwaggerResource(instantName,body);
}
// 根据原SwaggerResource list和服务名,转化得到新的SwaggerResource list
private List<SwaggerResource> exchangeListSwaggerResource(String instantName, List<Map> swaggerResourceList){
List reList = new ArrayList<SwaggerResource>(2);
if(swaggerResourceList!=null) {
for (Map map : swaggerResourceList) {
reList.add(this.newSwaggerResource(instantName, map));
}
}
return reList;
}
//创建SwaggerResource
private SwaggerResource newSwaggerResource(String instantName, Map map){
SwaggerResource reSwaggerResource = new SwaggerResource();
if(instantName==null||"".equals(instantName)) {
return reSwaggerResource;
}
reSwaggerResource.setName(instantName+"/"+map.get("name"));
reSwaggerResource.setLocation("/"+instantName.toLowerCase()+map.get("location"));
reSwaggerResource.setUrl("/"+instantName.toLowerCase()+map.get("url"));
reSwaggerResource.setSwaggerVersion(String.valueOf(map.get("swaggerVersion")));
return reSwaggerResource;
}
}
3. spring cloud gateway和zuul聚合swagger的差异
zuul:
对于zuul,只需要在启动类上标注@EnableSwagger2注解,并添加上述类,即可将各服务的swagger文档集成到网关,直接调用网关的地址和端口即可。
spring cloud gateway:
由于webflux和swagger同时使用会产生冲突,在spring cloud gateway上聚合swagger时,不能使用
@EnableSwagger2注解启用swagger,在添加上述类后,还需要重新写一个controller为前端提供接口服务。
该controller代码如下:
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerResourceController {
private GateSwaggerResourceProvider swaggerResourceProvider;
@Autowired
public SwaggerResourceController(GateSwaggerResourceProvider swaggerResourceProvider) {
this.swaggerResourceProvider = swaggerResourceProvider;
}
@RequestMapping(value = "/configuration/security")
public ResponseEntity<SecurityConfiguration> securityConfiguration() {
return new ResponseEntity<>(SecurityConfigurationBuilder.builder().build(), HttpStatus.OK);
}
@RequestMapping(value = "/configuration/ui")
public ResponseEntity<UiConfiguration> uiConfiguration() {
return new ResponseEntity<>(UiConfigurationBuilder.builder().build(), HttpStatus.OK);
}
@RequestMapping
public ResponseEntity<List<SwaggerResource>> swaggerResources() {
return new ResponseEntity<>(swaggerResourceProvider.get(), HttpStatus.OK);
}
}