微服务与中间件系列——GateWay整合Swagger3增强Knife4j
- GateWay整合Swagger3增强Knife4j(easy模式)
- 目的
- 服务端
- 1.导入依赖
- 2.编写配置类
- 3.yaml配置
 
- GateWay网关
- 1.文档枚举
- 2.SwaggerProvider
- 3.yaml配置
 
- 结果
 
- 增强版
- 服务端
- 1.增加配置参数类
- 2.修改配置类
- 3.修改yaml配置
 
- GateWay网关
- 1.增加全局认证过滤器
 
 
GateWay整合Swagger3增强Knife4j(easy模式)
目的
统一进行文档管理,查看,避免swagger文档在微服务中使用繁琐的问题
 本案例使用nacos作为服务的注册中心
服务端
1.导入依赖
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
2.编写配置类
package cn.fly.client.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
/**
 * @author Syf200208161018
 * @date 2022/11/19 15:44
 * @ClassName:SwaggerConfig
 * @Effect:SwaggerConfig is used for
 */
@Configuration
@EnableOpenApi
@Data
@ConfigurationProperties(prefix = "swagger")
public class SwaggerConfig {
    /**
     * 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
     */
    private Boolean enable;
    /**
     * 项目应用名
     */
    private String applicationName;
    /**
     * 项目版本信息
     */
    private String applicationVersion;
    /**
     * 项目描述信息
     */
    private String applicationDescription;
    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.OAS_30)
                .pathMapping("/")
                // 定义是否开启swagger,false为关闭,可以通过变量控制,线上关闭
                .enable(enable)
                //配置api文档元信息
                .apiInfo(apiInfo())
                // 选择哪些接口作为swagger的doc发布
                .select()
                //apis() 控制哪些接口暴露给swagger,
                // RequestHandlerSelectors.any() 所有都暴露
                // RequestHandlerSelectors.basePackage("net.xdclass.*")  指定包位置
                // withMethodAnnotation(ApiOperation.class)标记有这个注解 ApiOperation
                .apis(RequestHandlerSelectors.basePackage("cn.fly.client.controller.common"))
                .paths(PathSelectors.any())
                .build();
    }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(applicationName)
                .description(applicationDescription)
                .contact(new Contact("syf", "syf20020816@outlook.com", "syf20020816@outlook.com"))
                .version(applicationVersion)
                .build();
    }
}
3.yaml配置
- 配置服务名称
- 配置mvc路径策略
- 配置nacos路径
- 配置swagger(自定义非官方,看配置类就知道了)
spring:
  application:
    name: Eprop-client
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  cloud:
    nacos:
#      server-addr: 192.168.31.149:8848
      server-addr: 172.16.216.47:8848
swagger:
  enable: true
  application-name: Eprop-client
  application-version: 1.0.0
  application-description: client
GateWay网关
1.文档枚举
package cn.fly.gateway.swagger;
import io.netty.channel.nio.AbstractNioByteChannel;
/**
 * @author Syf200208161018
 * @date 2022/11/19 19:12
 * @ClassName:DocEnum
 * @Effect:DocEnum is used for
 */
public enum DocEnum {
    /**
     * 路由信息
     */
    EPROP_COMMON("Eprop-client","eprop-API模块");
    private String routeId;
    private String swaggerInfo;
    DocEnum(String routeId, String swaggerInfo) {
        this.routeId = routeId;
        this.swaggerInfo = swaggerInfo;
    }
    public String getRouteId() {
        return routeId;
    }
    public void setRouteId(String routeId) {
        this.routeId = routeId;
    }
    public String getSwaggerInfo() {
        return swaggerInfo;
    }
    public void setSwaggerInfo(String swaggerInfo) {
        this.swaggerInfo = swaggerInfo;
    }
    public static String getSwaggerInfoByRouteId(String routeId){
        for (DocEnum value : DocEnum.values()) {
            if (value.getRouteId().equals(routeId)){
                return value.getSwaggerInfo();
            }
        }
        return null;
    }
}
2.SwaggerProvider
实现SwaggerResourcesProvider对资源进行修改
package cn.fly.gateway.swagger;
import com.alibaba.cloud.commons.lang.StringUtils;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
 * @author Syf200208161018
 * @date 2022/11/19 16:55
 * @ClassName:SwaggerProvider
 * @Effect:SwaggerProvider is used for
 */
/**
 * 聚合系统接口
 * @author ROCKY
 */
@Component
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {
    public static final String API_URI = "/v3/api-docs";
    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;
    public SwaggerProvider(RouteLocator routeLocator, GatewayProperties gatewayProperties) {
        this.routeLocator = routeLocator;
        this.gatewayProperties = gatewayProperties;
    }
    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        // 取出gateway的route
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
        // 结合配置的route-路径(Path),和route过滤,只获取在枚举中说明的route节点
        gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
                .forEach(routeDefinition -> routeDefinition.getPredicates().stream()
                        // 目前只处理Path断言  Header或其他路由需要另行扩展
                        .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                        .forEach(predicateDefinition -> {
                                    String routeId = routeDefinition.getId();
                                    String swaggerInfo = DocEnum.getSwaggerInfoByRouteId(routeId);
                                    if (StringUtils.isNotEmpty(swaggerInfo)) {
                                        resources.add(swaggerResource(swaggerInfo, predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", API_URI)));
                                    }
                                }
                        ));
        return resources;
    }
    private SwaggerResource swaggerResource(String name, String location) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion("3.0");
        return swaggerResource;
    }
}
3.yaml配置
-  注意路由的ID要对应服务的名称 
  
-  注意过滤器的 StripPrefix=3一定要和你前面指定的断言- Path=/api/v1/eprop/**匹配上,比如我这个就是去除3个前缀,如果是/api/hello/**,就是去除2个前缀,这个一定不能错,因为这和swagger请求地址有关
  
server:
  port: 21301
spring:
  application:
    name: gateway
  cloud:
    nacos:
#      server-addr: 192.168.31.149:8848
      server-addr: 172.16.216.47:8848
#     server-addr: 192.168.1.103:8848
    gateway:
      routes:
        - id: Eprop-client
          uri: lb://Eprop-client
          predicates:
            - Path=/api/v1/eprop/**
          filters:
            - StripPrefix=3
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站进行跨域请求
              - "http://localhost:8080"
            allowedMethods:
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许所有请求头
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 跨域监测有效期
        add-to-simple-url-handler-mapping: true #解决options请求拦截
      discovery:
        locator:
          lower-case-service-id: true
结果
启动gateway浏览器访问ip:端口/doc.html
 成功如下:
 
增强版
其实也就是做了一下对于配置的处理
服务端
1.增加配置参数类
package cn.fly.client.properties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
 * @author Syf200208161018
 * @date 2022/11/20 0:44
 * @ClassName:SwaggerProperties
 * @Effect:SwaggerProperties is used for
 */
@ConfigurationProperties(prefix = "swagger-api")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SwaggerProperties {
    private String title="EPROP-API文档";
    private String groupName = "default";
    private String des="default API doc";
    private String version="1.1.0";
//    private Contact contact = new Contact("syf","syf20020816@outlook.com","syf20020816@outlook.com");
    private String docMaker = "syf";
    private String docUrl = "syf20020816@Outlook.com";
    private String makerEmail="syf20020816@Outlook.com";
    private String basePackage="cn.fly.client.controller";
    //解析的url规则
    private List<String> basePath = new ArrayList<>();
    //需要排除的url
    private List<String> excludePath = new ArrayList<>();
    /**
     * 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
     */
    private Boolean enable;
    /**
     * 项目应用名
     */
    private String applicationName;
    /**
     * 项目版本信息
     */
    private String applicationVersion;
    /**
     * 项目描述信息
     */
    private String applicationDescription;
}
2.修改配置类
package cn.fly.client.config;
import cn.fly.client.properties.SwaggerProperties;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
/**
 * @author Syf200208161018
 * @date 2022/11/19 15:44
 * @ClassName:SwaggerConfig
 * @Effect:SwaggerConfig is used for
 */
@Configuration
@EnableOpenApi
@EnableConfigurationProperties(SwaggerProperties.class)
public class SwaggerConfig {
    @Autowired
    private SwaggerProperties swaggerProperties;
    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.OAS_30)
                .pathMapping("/")
                // 定义是否开启swagger,false为关闭,可以通过变量控制,线上关闭
                .enable(swaggerProperties.getEnable())
                //配置api文档元信息
                .apiInfo(apiInfo())
                // 选择哪些接口作为swagger的doc发布
                .select()
                //apis() 控制哪些接口暴露给swagger,
                // RequestHandlerSelectors.any() 所有都暴露
                // RequestHandlerSelectors.basePackage("net.xdclass.*")  指定包位置
                // withMethodAnnotation(ApiOperation.class)标记有这个注解 ApiOperation
                .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
                .paths(PathSelectors.any())
                .build();
    }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getApplicationDescription())
                .contact(new Contact(swaggerProperties.getDocMaker(),swaggerProperties.getDocUrl(),swaggerProperties.getMakerEmail()))
                .version(swaggerProperties.getApplicationVersion())
                .build();
    }
}
3.修改yaml配置
swagger-api:
  enable: true
  application-name: Eprop-client
  application-version: 1.0.0
  application-description: Eprop API文档Swagger3版本
  title: EPROP-API 文档
  groupName: Eprop-client
  des: Eprop API文档Swagger3版本
  version: 1.0.0
  docMaker: syf
  docUrl: www.eprop.fly.cn
  markerEmail: syf20020816@Outlook.com
  basePackage: cn.fly.client.controller
GateWay网关
1.增加全局认证过滤器
package cn.fly.gateway.filter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
/**
 * @author Syf200208161018
 * @date 2022/10/28 13:16
 * @ClassName:AuthorizeFilter
 * @Effect:AuthorizeFilter is used for filtering user
 * the request header must have a token-eprop and then getUserInfo
 */
@Component
@Order(-1)
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println(exchange.getRequest().getURI().getPath());
            if (exchange.getRequest().getURI().getPath().equals("/v3/api-docs")){
                return chain.filter(exchange);
            }else{
                //get request params
                final ServerHttpRequest request = exchange.getRequest();
                //get header: token-eprop
                final HttpHeaders requestHeaders = request.getHeaders();
                final List<String> values = requestHeaders.get("token-eprop");
                for (String value : values) {
                    System.out.println(value);
                }
                final String tokenValue = values.get(0);
                System.out.println(tokenValue);
                //if tokenValue is empty or null intercept the request
                if (StringUtils.isBlank(tokenValue)){
                    return exchange.getResponse().setComplete();
                }
                return chain.filter(exchange);
            }
    }
}
其他无需更改

















