swagger版本如下

<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
<dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.6</version>
        </dependency>

自定义swagger接口相关代码

package com.example.gateway.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;

import java.util.Optional;

@RestController
public class SwaggerController {

    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;

    @Autowired(required = false)
    private UiConfiguration uiConfiguration;

    private final SwaggerResourcesProvider swaggerResources;

    @Autowired
    public SwaggerController(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }

    @GetMapping("/swagger-resources/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/swagger-resources/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/swagger-resources")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }

    @GetMapping("/")
    public Mono<ResponseEntity> swaggerResourcesN() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }

    @GetMapping("/csrf")
    public Mono<ResponseEntity> swaggerResourcesCsrf() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}

自定义组件构建swagger文档接口数据,代码如下

package com.example.gateway.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.example.gateway.dto.SwaggerResourceResponseItem;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Flux;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author xiaogang
 */
@Configuration
@Slf4j
public class HttpGetSwaggerResourceProvider implements SwaggerResourcesProvider {

    public static final String SWAGGER_RESOURCES = "/swagger-resources";
    private final RouteLocator routeLocator;

    @Value("${spring.application.name}")
    private String self;

    @Value("${server.port}")
    private int port;


    public HttpGetSwaggerResourceProvider(RouteLocator routeLocator) {
        this.routeLocator = routeLocator;
    }

    private Set<String> locationSet;

    @Override
    public List<SwaggerResource> get() {
        locationSet = Sets.newHashSet();

        List<SwaggerResource> swaggerResourceList = Lists.newLinkedList();
        List<Route> routeHosts = new ArrayList<>();
        Flux<Route> routes = routeLocator.getRoutes();
        routes.filter(route -> route.getUri().getHost() != null)
                .filter(route -> !self.equals(route.getUri().getHost()))
                .subscribe(route -> routeHosts.add(route));

        // 记录已经添加过的server,存在同一个应用注册了多个服务在nacos上
        for (Route route : routeHosts) {
            List<SwaggerResource> swaggerResources = this.requestSwaggerResource(route);
            if (CollUtil.isEmpty(swaggerResources)) {
                continue;
            }
            swaggerResourceList.addAll(swaggerResources);
        }
        return swaggerResourceList;
    }

    private List<SwaggerResource> requestSwaggerResource(Route route){
        String instance = route.getUri().getHost();
        String getSwaggerResources = new StringBuilder("http://localhost").append(":").append(port).append("/").append(instance).append(SWAGGER_RESOURCES).toString();
        HttpResponse httpResponse = HttpUtil.createGet(getSwaggerResources).execute();
        boolean success = 200 == httpResponse.getStatus();
        if (!success) {
            log.error("获取接口文档数据失败");
            return null;
        }
        String body = httpResponse.body();
        JSONArray objects;
        try {
            objects = JSONUtil.parseArray(body);
        } catch (Exception e) {
            log.error("获取接口文档数据失败");
            return null;
        }
        List<SwaggerResourceResponseItem> swaggerResourceList = objects.toList(SwaggerResourceResponseItem.class);
        if (CollUtil.isEmpty(swaggerResourceList)) {
            return null;
        }
        List<SwaggerResource> resources = Lists.newArrayList();
        for (SwaggerResourceResponseItem swaggerResourceResponseItem : swaggerResourceList) {
            String location = swaggerResourceResponseItem.getLocation();
            if (locationSet.contains(location)) {
                continue;
            }
            locationSet.add(location);
            SwaggerResource swaggerResource = new SwaggerResource();
            swaggerResource.setUrl(new StringBuilder("/").append(instance).append(location).toString());
            swaggerResource.setName(swaggerResourceResponseItem.getName());
            swaggerResource.setSwaggerVersion(swaggerResourceResponseItem.getSwaggerVersion());
            resources.add(swaggerResource);
        }
        return resources;
    }

}

请求接口获取的swagger数据结构如下

public class SwaggerResourceResponseItem{
	private String swaggerVersion;
	private String name;
	private String location;
}

代码就是这些,下面是关于开发过程中,前端怎么查看接口文档,以及生产环境中怎么屏蔽直接通过服务名称访问到接口文档或者接口的一些建议

首先,开发环境可以添加如下配置自动获取注册中心的所有其它服务的接口文档。

spring.cloud.gateway.discovery.locator.enabled=true

这段配置的意思是,开启gateway自动获取注册中心的服务,并且默认通过服务名称可以路由到该服务的配置。例如有一个用户服务为user,那么可以通过http://user/doc.html访问user服务的接口文档而不用每个服务都去配置路由。

但是生产环境不希望暴露出没有配置的服务给外部访问,则不要添加这段配置。但是怎么解决前端在接入完成之后不修改代码而平滑的过度到生产环境呢。我想到的方案是,开发环境还是通过服务名称访问服务,但是前段同时要传递一个header,名称比如为serviceName,那么值就是即将要访问的服务的名称,这样,生产环境可以对header中的服务名称进行转发

注意,上面是gateway的swagger集成方法,在其它微服务,文档的生产还是和单体一样。只不过如果要在服务层对接口进行分组,那么一定要注意

RequestHandlerSelectors.basePackage的设置,这里面的值不是指精确的包名,而实际上是一个通配符,如果写的很短,比如us,那么swagger会吧所有以us开头的包下面的所有类都作为生成接口文档数据的对象。比如user就在这个范围中。结果就是本来要分组,但是数据都跑到另外一个组当中了
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐