SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题

写在前边

当你看到这篇文章时,默认已经整合好了Springcloud/gateway/springfox(swaggerui3), 仅仅是在进行测试服务时出现问题。

问题描述

通过swagger-ui页面访问时会忽略服务名直接访问swagger映射出的接口,但是我们是通过网关转发的,这么一访问就直接404;如下所示:

SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题
出现这个问题,就是Servers映射没有达到我们的要求,我需要的是加上服务名的servers,但是默认却只有网关的ip和端口,没有当前的服务名;如下图所示:
SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题
很奇怪,我尝试通过在swagger配置中添加servers来改变这个Inferred Url,但是一直无效,debug后发现在AbstractDocumentationPluginsBootstrapper类中第96行执行之后就没有了,导致在请求时没有servers,然后swagger会创建一个默认的servers,这个默认的就是网关的ip+端口。
SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题

现在问题已经找到了,要么修改这个scan方法,要么在请求时构建默认的 Inferred Url 时做手脚,看代码后发现请求时改这个创建默认的Inferred Url 比较简便,就修改了这个创建方式;默认的调用和创建方式如下:

SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题
SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题

解决方案

那么这样就可以创建一个完整包路径与它一样的包,自己创建一个名字一样的类来覆盖框架中的类,然后在创建Inferred Url时添加上服务名,服务名Swagger已经放到请求头中了,取出来添加上就可以了。
SpecGeneration.java

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;

/**
 * 尝试用该类替换框架中的类
 *
 * @author vains
 * @date 2021/4/6 11:21
 */
@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;
        }
    }
}

注意,包的路径一定不能错!!然后这个类只要保证配置swagger的项目能够访问就可以了

SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题

效果

然后打开/swagger-ui/页面,就发现服务名已经添加成功了。

SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题
尝试请求一个接口,路径也正确。
SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题
至此,问题解决。

我的swagger配置

配置参考:springcloud gateway 整合swagger3.0.0
网关中的配置:
SwaggerProvider.java

import com.vains.config.properties.ApplicationNameConfig;
import lombok.AllArgsConstructor;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

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

/**
 * swagger3接口文档配置
 *
 * @author vains
 * @date 2021/4/5 2:42
 */
@Primary
@Configuration
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {

    /**
     * swagger2默认的url后缀
     */
    public static final String SWAGGER2URL = "/v2/api-docs";

    /**
     * swagger3默认的url后缀
     *  要使用ui的话 改成v2 不然会出bug  比如有的地方 没有输入框
     */
    public static final String SWAGGER3URL = "/v3/api-docs";
    /**
     * 网关路由
     */
    private final RouteLocator routeLocator;

    /**
     * 网关应用名称
     */
    private ApplicationNameConfig applicationNameConfig;

    /**
     * 对于gateway来说这块比较重要 让swagger能找到对应的服务
     *
     * @return 服务列表
     */
    @Override
    public List get() {
        List resources = new ArrayList<>();
        List routeHosts = new ArrayList<>();
        // 获取所有可用的host:serviceId
        routeLocator.getRoutes().filter(route -> route.getUri().getHost() != null)
                .filter(route -> !applicationNameConfig.getName().equals(route.getUri().getHost()))
                .subscribe(route -> routeHosts.add(route.getUri().getHost()));

        // 记录已经添加过的server
        Set added = new HashSet<>();
        routeHosts.forEach(instance -> {
            // 拼接url
            String url = "/" + instance.toLowerCase() + SWAGGER3URL;
            if (!added.contains(url)) {
                added.add(url);
                SwaggerResource swaggerResource = new SwaggerResource();
                swaggerResource.setUrl(url);
                swaggerResource.setName(instance);
                resources.add(swaggerResource);
            }
        });
        return resources;
    }

}

ApplicationNameConfig.java

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 获取应用名称
 *
 * @author vains
 * @date 2021/4/5 2:44
 */
@Data
@Component
@ConfigurationProperties(prefix = ApplicationNameConfig.PREFIX)
public class ApplicationNameConfig {

    static final String PREFIX = "spring.application";

    /**
     * 应用名称
     */
    private String name;

}

其它微服务中的Swagger配置:
SwaggerConfig.java

import com.vains.config.properties.SwaggerProperties;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestHeader;
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.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.Collections;

/**
 * swagger3配置
 *
 * @author vains
 * @date 2021/4/5 2:36
 */
@EnableOpenApi
@Configuration
@AllArgsConstructor
public class SwaggerConfig {

    private final SwaggerProperties swaggerProperties;

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30)
            .enable(swaggerProperties.getEnable())
            .host(swaggerProperties.getTryHost())
            .select()
            .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
            .paths(PathSelectors.any())
            .build().apiInfo(
                new ApiInfoBuilder()
                    .title(swaggerProperties.getApplicationName() + " Restful Apis.")
                    .description(swaggerProperties.getApplicationDescription())
                    .version(swaggerProperties.getApplicationVersion())
                    .termsOfServiceUrl(swaggerProperties.getTryHost())
                    .build())
            // 忽略注解
            .ignoredParameterTypes(RequestHeader.class)
            // Bearer Token 加入 swagger
            .securitySchemes(Collections.singletonList(
                new ApiKey("Authorization", "Authorization", "header")
            ))
            .securityContexts(Collections.singletonList(
                SecurityContext.builder()
                    .securityReferences(Collections.singletonList(SecurityReference.builder()
                            .scopes(new AuthorizationScope[0])
                            .reference("Authorization")
                            .build()))
                    .operationSelector(o ->
                            o.requestMappingPattern().matches("/.*")
                    )
                    .build()
                )
            );
    }

}

SwaggerProperties.java

package com.vains.config.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 自定义swagger3属性
 *
 * @author vains
 * @date 2021/4/5 2:36
 */
@Data
@Component
@ConfigurationProperties(prefix = SwaggerProperties.PREFIX)
public class SwaggerProperties {

    static final String PREFIX = "swagger";

    /**
     * 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
     */
    private Boolean enable;

    /**
     * 项目应用名
     */
    private String applicationName;

    /**
     * 项目版本信息
     */
    private String applicationVersion;

    /**
     * 项目描述信息
     */
    private String applicationDescription;

    /**
     * 接口调试地址
     */
    private String tryHost;

}

如果有什么疑问或更好的解决方案可在评论区留言,谢谢。

Original: https://www.cnblogs.com/vains/p/16207443.html
Author: 天玺
Title: SpringCloud Gateway 整合Springfox/SwaggerUI3 之后调用某一个服务的接口时,请求路径不会加上对应的服务名问题

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/608366/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • YSPASYS 中小型企业简单员工评价考核系统

    背景:公司运营接近2年时间了,随着不断的有员工入职、离职,使用信息化管理员工各类信息是一件很有必要的事儿。诸如员工基本信息,内部公告,资产盘点,客户管理,工作周报,优秀员工评选,请…

    Java 2023年6月9日
    086
  • Jenkins发布SpringBoot项目

    第二步:Configure System (系统设置) 我们只需要设置最后面的一项,配置远程服务SSH: 配置完成后点击保存即可,为后面我们配置自动化部署做准备,配置如下图: 第三…

    Java 2023年6月5日
    093
  • Java开发学习(十七)—-AOP案例之测量业务层接口执行效率

    一、需求分析 这个需求比较简单 需求:任意业务层接口执行均可显示其执行效率(执行时长) 这个的目的是查看每个业务层执行的时间,这样就可以监控出哪个业务比较耗时,将其查找出来方便优化…

    Java 2023年5月29日
    094
  • Java–深克隆

    深克隆和浅克隆 浅克隆 有原型对象Object,它是引用类型,然后拷贝后产生两个克隆对象obj1和obj2,只是它们克隆的是Object的内存地址,因此ob1和obj2都指向Obj…

    Java 2023年6月13日
    081
  • Springcloud学习笔记49–Springboot读取外部配置文件,避免更新jar包内配置文件重新打包部署

    1 Springboot读取外部配置文件优先级 如果springBoot项目与配置文件不分离,那么每次修改配置文件都需要重新重新打包部署应用,十分麻烦。解决方法是让springbo…

    Java 2023年5月30日
    0106
  • Java8 新特性学习

    1、接口中的默认方法 Java8中允许接口中包含具有具体实现的方法,这种方法被称为”默认方法”,使用default关键字修饰。 如: public inte…

    Java 2023年6月8日
    087
  • C C++指针面试题零碎整理

    int a; int* p = &a; 答:p指向a的地址,&是取a的地址。*指的是指针中取内容的符号。 2.str[]和str*的区别: char str1[] …

    Java 2023年6月16日
    077
  • Spring Security认证流程分析(6)

    1.认证流程分析 Spring Security中默认的一套登录流程是非常完善并且严谨的。但是项目需求非常多样化, 很多时候,我们可能还需要对Spring Secinity登录流程…

    Java 2023年6月13日
    054
  • (转发)远程桌面连接无显示器的 Linux(Ubuntu/Debian等):使用 Xfce

    手头上有台电脑,安装了 Ubuntu,安装了 postgres 数据库,用于开发机,未连接显示器。在 Windows 开发机上,无法通过 VNC, Anydesk 连接远程桌面到 …

    Java 2023年6月9日
    079
  • Jenkins+gitlab+docker+harbor容器化自动部署详细流程

    环境:Linux版本:Centos7 一、更新源:yum update 二、安装docker:yum install docker -y 启动docker: systemctl s…

    Java 2023年6月13日
    078
  • JAVA基础学习第五天!

    精华笔记: 1.循环结构: -for结构:应用率高、与次数相关的循环 1 )语法: // 1 2 3 for (要素1;要素2;要素3){ 语句块/循环体————-…

    Java 2023年6月13日
    069
  • 在Ubuntu机器上使用war包安装Jenkins

    因为一些需求需要迁移之前使用的Jenkins,原来是按照官方文档使用apt方式安装的,这次搬迁后的机器由于默认不通外网(可以通过代理走外网),因此趁此机会,尝试改用war包方式安装…

    Java 2023年6月14日
    083
  • 方法理论学习

    什么是方法 方法在很多地方又称作函数,方法是为完成一个操作而组合在一起的语句组 好处:可以省略编写重复代码;可以组织和简化代码;提高代码的可读性 方法的种类 内置方法 由JDK类库…

    Java 2023年6月6日
    081
  • maven概述和maven依赖管理的概念以及一件构成的概念

    我们的项目,往往都要经历编译、测试、运行、打包、安装 ,部署等一系列过程。 什么是构建? 指的是项目从编译、测试、运行、打包、安装 ,部署整个过程都交给 maven 进行管理,这个…

    Java 2023年6月6日
    084
  • 为什么说不变模式可以提高性能

    在Java中基础类型的包装类都是不可变的类,如Boolean、Byte、Character、Double、Float、Integer、Long、Short,另外还有String。这…

    Java 2023年6月6日
    080
  • 在2007年戴尔 Inspiron 1501 旧笔记本电脑上安装 Debian, 用做家里老人的上网本

    手头上有一台戴尔 Inspiron 1501 旧笔记本电脑,2G 内存、250G 硬盘,AMD 芯片,2007年买的,想给家里老人做上网本。 安装了 Windows 10 家庭版,…

    Java 2023年6月9日
    068
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球