美文网首页js css html
gateway整合swagger3.0+knife4j-spri

gateway整合swagger3.0+knife4j-spri

作者: 无我_无他_有你 | 来源:发表于2022-07-26 17:02 被阅读0次

    1.子服务整合swagger

    引入依赖

    <swagger-version>3.0.0</swagger-version>
    <knife4j-spring-ui-version>3.0.3</knife4j-spring-ui-version>
    <dependencies>
             <!-- 整合swagger start -->
             <!-- 3.0版本只需要引入一个,不再像2.x版本需要引入两个包 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-boot-starter</artifactId>
                <version>${swagger-version}</version>
            </dependency>
            <!-- ui美化依赖 -->
                <!--swagger UI-->
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-spring-ui</artifactId>
                <version>${knife4j-spring-ui-version}</version>
            </dependency>
            <!-- 整合swagger end -->
    </dependencies>
    

    swagger配置文件

    package com.ly.tulip.commons.swagger.config;
    
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.builders.RequestParameterBuilder;
    import springfox.documentation.oas.annotations.EnableOpenApi;
    import springfox.documentation.schema.ScalarType;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Contact;
    import springfox.documentation.service.ParameterType;
    import springfox.documentation.service.RequestParameter;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 类说明: swagger 配置类
     *
     * @author wqf
     * @date 2022/7/26 10:23
     */
    @Component
    @EnableOpenApi
    public class SwaggerConfig {
    
        /**
         * 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
         */
        @Value("${swagger.enable:false}")
        private Boolean enable;
    
        /**
         * 项目应用名
         */
        @Value("${swagger.application-name}")
        private String applicationName;
    
        /**
         * 项目版本信息
         */
        @Value("${swagger.application-version}")
        private String applicationVersion;
    
        /**
         * 项目描述信息
         */
        @Value("${swagger.application-description}")
        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(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                    .paths(PathSelectors.any())
                    .build()
                    .globalRequestParameters(getGlobalRequestParameters());
        }
    
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title(applicationName)
                    .description(applicationDescription)
                    .contact(new Contact("xxxx平台接口文档", "www.xxx.com", "xxx.com"))
                    .version(applicationVersion)
                    .build();
        }
    
    
        //添加全局通用参数 一般用添加请求token
        private List<RequestParameter> getGlobalRequestParameters() {
            List<RequestParameter> parameters = new ArrayList<>();
            parameters.add(new RequestParameterBuilder()
                    .name("Authentication")
                    .description("身份令牌")
                    .required(true)
                    //添加参数到请求头中
                    .in(ParameterType.HEADER)
                    .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
                    .required(false)
                    .build());
            return parameters;
        }
      }
    }
    

    yml配置swagger相关信息

    swagger:
      enable: true
      application-name: 网关服务
      application-version: 1.0
      application-description: 网关服务
    

    配置完,访问测试地址: http://ip:端口号/doc.html#/home

    在微服务中存在多个服务,可以将以上配置部分做成公共模块,哪个模块需要引入swagger,引入该公共模块就行了。

    2.整合到gateway

    1.引入依赖 ,同上
    2.gateway模块添加两配置文件,如下

    SwaggerHandler

    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.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import reactor.core.publisher.Mono;
    import springfox.documentation.swagger.web.SecurityConfiguration;
    import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
    import springfox.documentation.swagger.web.SwaggerResourcesProvider;
    import springfox.documentation.swagger.web.UiConfiguration;
    import springfox.documentation.swagger.web.UiConfigurationBuilder;
    
    import java.util.Optional;
    
    /**
     * SWAGGER-RESOURCES控制对象
     * @author ROCKY
     */
    @RestController
    @RequestMapping("/swagger-resources")
    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("/configuration/security")
        public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
            return Mono.just(new ResponseEntity<>(
                    Optional.ofNullable(securityConfiguration)
                            .orElse(SecurityConfigurationBuilder.builder().build()),
                    HttpStatus.OK));
        }
    
        @GetMapping("/configuration/ui")
        public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
            return Mono.just(new ResponseEntity<>(
                    Optional.ofNullable(uiConfiguration)
                            .orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
        }
    
        @SuppressWarnings("rawtypes")
        @GetMapping("")
        public Mono<ResponseEntity> swaggerResources() {
            return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
        }
    }
    

    SwaggerResourcesProvider

    
    import lombok.AllArgsConstructor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.gateway.config.GatewayProperties;
    import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    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 wqf
     * @date 2022/7/26 16:47
     */
    @Component
    @Primary
    @AllArgsConstructor
    public class SwaggerProvider implements SwaggerResourcesProvider {
        // SWAGGER3默认的URL后缀
        public static final String SWAGGER3URL = "/v3/api-docs";
        // 网关路由
        @Autowired
        private RouteLocator routeLocator;
    
        @Autowired
        private GatewayProperties gatewayProperties;
    
        // 聚合其他服务接口
        @Override
        public List<SwaggerResource> get() {
            List<SwaggerResource> resourceList = new ArrayList<>();
            List<String> routes = new ArrayList<>();
            // 获取网关中配置的route
            routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
            for (RouteDefinition routeDefinition : gatewayProperties.getRoutes()) {
                if (routes.contains(routeDefinition.getId())) {
                    for (PredicateDefinition predicateDefinition : routeDefinition.getPredicates()) {
                        if ("Path".equalsIgnoreCase(predicateDefinition.getName())) {
                            resourceList.add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs()
                                            .get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", SWAGGER3URL)));
                        }
                    }
                }
            }
            return resourceList;
        }
    
        private SwaggerResource swaggerResource(String name, String location) {
            SwaggerResource swaggerResource = new SwaggerResource();
            swaggerResource.setName(name);
            swaggerResource.setLocation(location);
            swaggerResource.setSwaggerVersion("3.0");
            return swaggerResource;
        }
    }
    

    注意点:

    1. 网关对:/swagger-ui/**; /v3/api-docs 等swagger访问路径记得放行

    2. 网关配置文件需要配置路由规则,以下配置是按服务名称进行路由转发的,好处是服务端口改了也没事。

    server:
      port: 10000
    spring:
      profiles:
        active: dev
      application:
        name: gateway-server
      #配置网关路由规则 按服务名进行路由
      cloud:
        gateway:
          discovery:
            locator:
              # 是否与服务发现组件结合,通过serviceId 转发到具体的服务实例
              enabled: true #是否开启基于服务发现的路由规则
              lower-case-service-id: true # 是否将服务名称转小写
          routes:
            # 唯一标识 - 系统核心服务
            - id: base-server
              uri: lb://base-server
              #断言,路径相匹配的进行路由,配置服务端的方法路
              predicates:
                - Path=/base-server/**
              filters:
                - StripPrefix=0
            # 唯一标识 - 系统用户服务
            - id: auth-server
              uri: lb://auth-server
              predicates:
                - Path=/auth-server/**
              filters:
                - StripPrefix=0
    
    1. yml配置了几个服务的路由,则只有当所有服务都正常时,swagger访问页面才会正常,如果配置两个服务的路由规则,但只启动一个服务swagger是会报错的

    出现的问题:
    在swagger页面测试请求时,发现发出去的请求没有带上服务名导致访问404,如:http://127.0.0.1:10000/logout
    正确请求是:http://127.0.0.1:10000/auth-server/logout
    解决方法:java目录下创建目录 springfox.documentation.oas.web
    在该目录下创建文件SpecGeneration

    package springfox.documentation.oas.web;
    
    import io.swagger.v3.oas.models.servers.Server;
    import lombok.extern.slf4j.Slf4j;
    import org.slf4j.Logger;
    import org.springframework.util.StringUtils;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.UnsupportedEncodingException;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.net.URLDecoder;
    import java.nio.charset.StandardCharsets;
    
    import static org.slf4j.LoggerFactory.getLogger;
    
    /**
     * 类说明: 处理swagger 模拟请求时 没有追加服务名的问题
     *
     * @author wqf
     * @date 2022/7/26 16:29
     */
    @Slf4j
    public class SpecGeneration {
    
        private static final String HEADER_NAME = "X-Forwarded-Prefix";
        private static final Logger LOGGER = getLogger(SpecGeneration.class);
        public static final String OPEN_API_SPECIFICATION_PATH
                = "${springfox.documentation.open-api.v3.path:/v3/api-docs}";
        protected static final String HAL_MEDIA_TYPE = "application/hal+json";
    
        private SpecGeneration() {
            throw new UnsupportedOperationException();
        }
    
        /**
         * 创建一个默认的 swagger 的server
         *
         * @param requestPrefix /v3/api-docs
         * @param requestUrl    请求的url
         * @return Server
         */
        public static Server inferredServer(String requestPrefix, String requestUrl) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String serverUrl = requestUrl.replace(requestPrefix, "");
            String header = null;
            try {
                URI url = new URI(requestUrl);
                serverUrl = String.format("%s://%s:%s", url.getScheme(), url.getHost(), url.getPort());
                header = request.getHeader(HEADER_NAME);
                if (!StringUtils.isEmpty(header)) {
                    log.info("当前的服务为:{}", header);
                    serverUrl += header;
                }
            } catch (URISyntaxException e) {
                LOGGER.error("Unable to parse request url:" + requestUrl);
            }
            String description = "Inferred Url";
            if (!StringUtils.isEmpty(header)) {
                description += " For " + header.substring(1);
            }
            return new Server()
                    .url(serverUrl)
                    .description(description);
        }
    
        public static String decode(String requestURI) {
            try {
                return URLDecoder.decode(requestURI, StandardCharsets.UTF_8.toString());
            } catch (UnsupportedEncodingException e) {
                return requestURI;
            }
        }
    }
    

    2022/07/27 补充
    knife4j-spring-ui 依赖整合还是有点问题,可以先移除这个依赖,其他配置不变,swagger访问地址使用:
    http://ip:网关端口号/swagger-ui/index.html

    相关文章

      网友评论

        本文标题:gateway整合swagger3.0+knife4j-spri

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