• spring-cloud-gateway聚合swagger文档


    需求背景

    spring cloud搭建微服务系统,每个业务模块使用swagger开放文档接口查询,在业务网关模块提供swagger文档聚合查询接口,可以通过选择业务模块分类查看。

    框架选型、版本及主要功能

    1. spring boot 2.1.6.RELEASE
    2. spring cloud Greenwich.SR3
    3. spring cloud gateway 2.1.3.RELEASE 网关组件
    4. knife4j 2.0.1 增强swagger ui样式,网关使用其starter依赖
    5. swagger bootstrap ui 1.9.6 增强swagger ui样式
    6. spring4all-swagger 1.9.0.RELEASE 配置化swagger参数,免去代码开发

    模块职责划分

    1. swagger组件
      开发一个项目内的swagger-spring-boot-starter,整合swagger bootstrap ui 1.9.6和spring4all-swagger 1.9.0.RELEASE,对外提供@EnableSwagger注解服务

    2. 业务模块
      引用自定义的swagger-spring-boot-starter,同时在配置文件中添加本模块的swagger基础信息配置。

    3. 网关模块
      引用knife4j整合swagger,并开发filter、handler、config对多模块的swagger进行聚合

    开发步骤示例

    swagger组件

    pom.xml文件依赖

    <dependency>
    	<groupId>com.github.xiaoymin</groupId>
    	<artifactId>swagger-bootstrap-ui</artifactId>
    	<version>1.9.6</version>
    </dependency>
    <dependency>
    	<groupId>com.spring4all</groupId>
    	<artifactId>swagger-spring-boot-starter</artifactId>
    	<version>1.9.0.RELEASE</version>
    </dependency>
    

    自定义注解

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @EnableSwagger2Doc
    @EnableSwaggerBootstrapUI
    @Import(SwaggerCommandLineRunner.class)
    public @interface EnableSwagger {
    }
    

    注意@EnableSwagger2Doc,@EnableSwaggerBootstrapUI注解,@EnableSwagger2Doc注解能将swagger配置文档化,避免业务模块再开发swagger的代码,@EnableSwaggerBootstrapUI就是改进了swagger ui界面。

    指定swagger的默认访问端口

    @Slf4j
    @Component
    public class SwaggerCommandLineRunner implements CommandLineRunner {
    
        @Value("${server.port:8080}")
        private String serverPort;
    
        @Override
        public void run(String... args) {
            log.info("swagger url:http://localhost:" + serverPort + "/doc.html");
        }
    }
    

    这样这个组件就集成完毕了,这个组件以多module的形式存在于项目公共组件中,使用maven引用即可。

    业务模块开发

    application中使用@EnableSwagger注解(自行开发的那个,不要搞错了)

    @SpringBootApplication
    @EnableDiscoveryClient
    @MapperScan("com.hy.demo.**.mapper")
    @EnableSwagger
    public class DemoApplication {
    
    	public static void main(String[] args) {
    		ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
    		ctx.start();
    	}
    
    }
    

    配置文件加上swagger信息:

    swagger:
      enabled: true
      title: hy demo
      base-package: com.hy.demo
    

    如此业务模块部分就完成了,非常简单,代码零侵入

    网关开发

    网关的开发是重头戏,里面需要集成knife4j(这个框架是swagger bootstrap ui的最终版本,最终版改了个名字,开发是同一个人),并且对请求url进行适配

    pom.xml添加swagger的依赖

    <dependency>
    	<groupId>com.github.xiaoymin</groupId>
    	<artifactId>knife4j-spring-boot-starter</artifactId>
    	<version>2.0.1</version>
    </dependency>
    

    配置文件gateway部分,对业务接口配置和swagger配置要单独设置routes,例如:

    spring:
      application:
        name: gate
      cloud:
        gateway:
          discovery:
            locator:
              #enabled: true0
              enabled: false
              lower-case-service-id: true
          routes:
            - id: demo
              uri: lb://demo
              predicates:
                - Path=/api/json/hy/demo/**
    
            - id: demoSwagger
              uri: lb://demo
              predicates:
                - Path=/demo/**
              filters:
                - SwaggerHeaderFilter
                - StripPrefix=1
    

    我们假定业务转发的接口URL前缀定义:/api/json/hy/demo,demo为业务模块名称,业务接口对URL处理不需要加过滤器。
    route id后缀为Swagger结尾,需要添加StripPrefix过滤器和自定义过滤器SwaggerHeaderFilter。

    ResourceConfig开发,这个类的作用是从route信息里抽取业务模块信息,并且展示在swagger ui左上角的下拉框里

    /**
     *  获取SwaggerResources列表信息,即业务模块列表
     *  列表数据填充在swagger ui左上角的下拉框里
     * @description:
     * @author: demo
     * @create: 2020-02-25 17:15
     **/
    @Slf4j
    @Component
    @Primary
    @AllArgsConstructor
    public class SwaggerResourceConfig implements SwaggerResourcesProvider {
    
    	private static final String SWAGGER_URI = "/v2/api-docs";
    
    	private final RouteLocator routeLocator;
    	private final GatewayProperties gatewayProperties;
    
    
    	@Override
    	public List<SwaggerResource> get() {
    		List<SwaggerResource> resources = new ArrayList<>();
    		List<String> routes = new ArrayList<>();
    		// 只抽取后缀为Swagger的路由信息
    		routeLocator.getRoutes().filter(r -> r.getId().endsWith("Swagger")).subscribe(route -> routes.add(route.getId()));
    
    		gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
    			route.getPredicates().stream()
    					.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
    					.forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
    							predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
    									.replace("/**", SWAGGER_URI))));
    		});
    
    		return resources;
    	}
    
    	private SwaggerResource swaggerResource(String name, String location) {
    		log.info("name:{},location:{}",name,location);
    		SwaggerResource swaggerResource = new SwaggerResource();
    		swaggerResource.setName(name);
    		swaggerResource.setLocation(location);
    		swaggerResource.setSwaggerVersion("2.0");
    		return swaggerResource;
    	}
    }
    

    示例效果如下图:
    【图一】

    swagger ui静态资源的处理:

    /**
     * @description:
     * @author: demo
     * @create: 2020-02-25 17:19
     **/
    @RestController
    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("/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)));
    	}
    }
    

    swagger 过滤器示例代码

    /**
     * @description:
     * @author: demo
     * @create: 2020-02-25 17:01
     **/
    @Component
    public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
    	private static final String HEADER_NAME = "X-Forwarded-Prefix";
    
    	private static final String SWAGGER_URI = "/v2/api-docs";
    
    	@Override
    	public GatewayFilter apply(Object config) {
    		return (exchange, chain) -> {
    			ServerHttpRequest request = exchange.getRequest();
    			String path = request.getURI().getPath();
    			if (!StringUtils.endsWithIgnoreCase(path,SWAGGER_URI)) {
    				return chain.filter(exchange);
    			}
    			String basePath = path.substring(0, path.lastIndexOf(SWAGGER_URI));
    			ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
    			ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
    			return chain.filter(newExchange);
    		};
    	}
    }
    
    业务过滤器修改

    由于该组件是业务网关组件,肯定会有通用的过滤器,来完成token校验,身份证识别等功能,记得在这些过滤器将"/v2/api-docs"的URL放行,示例代码:

    if (StringUtils.endsWithIgnoreCase(path,"/v2/api-docs")) {
    	return chain.filter(exchange);
    }
    

    测试验证

    启动相应的注册中心、业务模块、网关模块进行验证,验证swagger文档、业务接口的处理能否同时满足。

    专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
    可以扫左边二维码添加好友,邀请你加入Java架构社区微信群共同探讨技术
    Java架构社区

  • 相关阅读:
    String、StringBuffer、StringBuilder
    动态规划引入—矩阵乘法
    flask中间件
    有状态服务,无状态服务
    python 工厂模式
    python 单例模式
    python 工厂模式
    python timedelta() 和relativedelta()的区别
    mongo 查看(集合)表结构
    logstash 实现数据源分流
  • 原文地址:https://www.cnblogs.com/huangying2124/p/13461556.html
Copyright © 2020-2023  润新知