美文网首页全栈程序猿的成长
【Knife4j】小试牛刀,基于gateway的集成

【Knife4j】小试牛刀,基于gateway的集成

作者: 小尘哥 | 来源:发表于2022-05-30 16:05 被阅读0次

    上回书说到,knife4j基于注册中心eureka集成,由于有些小伙伴可能使用了nacos、Consul、zk等注册中心,均有对应的集成方法,但是一旦切换了注册中心(比如从eureka切换成nacos),则需要重新做集成。因此本文介绍一下基于gateway的集成方式。

    一、工程结构

    • eboot-center:eureka注册中心(服务端)
      eboot-knife4j:文档服务,本文不需要
      eboot-common:包含了一些基础的认证、全局异常等处理,本文暂不需要
    • eboot-gateway:spring-cloud-gateway,大多是情况下微服务均走网关
    • eboot-modulars:即各个业务子系统,这里分了三个:认证管理、文件管理、系统管理
      eboo-ui:前端项目,本文不需要
      image.png

    二、center、modulars配置

    均参考上篇文章即可,可回头看《【Knife4j】小试牛刀,基于eureka的集成

    三、gateway聚合配置

    此处建议先看官网配置
    pom配置

          <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-spring-boot-starter</artifactId>
            </dependency>
    

    原springboot基于tomcat,而gateway基于webflux,因此需要以下操作:
    springfox-swagger提供的分组接口是swagger-resource,返回的是分组接口名称、地址等信息。在Spring Cloud微服务架构下,我们需要重写该接口,主要是通过网关的注册中心动态发现所有的微服务文档,代码如下:

    package com.mos.eboot.gateway.config.properties;
    
    import lombok.AllArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.gateway.config.GatewayProperties;
    import org.springframework.cloud.gateway.route.RouteLocator;
    import org.springframework.cloud.gateway.support.NameUtils;
    import org.springframework.context.annotation.Primary;
    import org.springframework.stereotype.Component;
    import springfox.documentation.swagger.web.SwaggerResource;
    import springfox.documentation.swagger.web.SwaggerResourcesProvider;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * swagger资源配置
     *
     * @author 小尘哥
     * @date 2022/05/30
     */
    @Slf4j
    @AllArgsConstructor
    @Component
    @Primary
    public class SwaggerResourceConfig implements SwaggerResourcesProvider {
    
        private final RouteLocator routeLocator;
        private final GatewayProperties gatewayProperties;
    
        @Override
        public List<SwaggerResource> get() {
            List<SwaggerResource> resources = new ArrayList<>();
            List<String> routes = new ArrayList<>();
            routeLocator.getRoutes().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("**", "v2/api-docs"))));
            });
            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;
        }
    }
    
    

    接口如下

    package com.mos.eboot.gateway.handler;
    
    import org.springframework.beans.factory.annotation.Autowired;
    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 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)));
        }
    }
    
    

    yaml配置(基于yaml中的routes进行分组)

    server:
      port: 9005
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
      instance:
        appname: eboot-gateway
        leaseRenewalIntervalInSeconds: 1
        lease-expiration-duration-in-seconds: 2
        prefer-ip-address: true
        instance-id: 127.0.0.1:9005
    spring:
      main:
        allow-bean-definition-overriding: true
        allow-circular-references: true
        web-application-type: REACTIVE
      cloud:
        gateway:
          discovery:
            locator:
              # gateway开启服务注册和发现的功能,
              enabled: true
              # 将请求路径上的服务名配置为小写(因为服务注册的时候,向注册中心注册时将服务名转成大写的了)
              lowerCaseServiceId: true
          routes:
            - id: boot-system
              uri: http://localhost:9093
              predicates:
                - Path=/system/**
           #   filters:
            #    - SwaggerHeaderFilter
           #     - StripPrefix=1
            - id: eboot-auth
              uri: http://localhost:9091
              predicates:
                - Path=/auth/**
    

    四、展示

    image.png

    五、注意点

    在集成Spring Cloud Gateway网关的时候,会出现没有basePath的情况(即定义的例如/user、/order等微服务的前缀),这个情况在使用zuul网关的时候不会出现此问题,因此,在Gateway网关需要添加一个Filter实体Bean

    由于我使用的springcloud版本为3.1.2,未出现该问题,若出现请参考官网的解决方案,添加以下过滤器,同时放开yaml中的routes.filters配置项

    @Component
    public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
        private static final String HEADER_NAME = "X-Forwarded-Prefix";
    
        private static final String 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,URI )) {
                    return chain.filter(exchange);
                }
                String basePath = path.substring(0, path.lastIndexOf(URI));
                ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
                ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
                return chain.filter(newExchange);
            };
        }
    }
    

    相关文章

      网友评论

        本文标题:【Knife4j】小试牛刀,基于gateway的集成

        本文链接:https://www.haomeiwen.com/subject/mgmoprtx.html