网关集成swagger

网关集成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);
    }

}

:+1: