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

转载 https://blog.csdn.net/weixin_43356507/article/details/115537098

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

写在前边

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

   
   
  • 1

问题描述

通过swagger-ui页面访问时会忽略服务名直接访问swagger映射出的接口,但是我们是通过网关转发的,这么一访问就直接404;如下所示:
例图1
出现这个问题,就是Servers映射没有达到我们的要求,我需要的是加上服务名的servers,但是默认却只有网关的ip和端口,没有当前的服务名;如下图所示:
例图2
很奇怪,我尝试通过在swagger配置中添加servers来改变这个Inferred Url,但是一直无效,debug后发现在AbstractDocumentationPluginsBootstrapper类中第96行执行之后就没有了,导致在请求时没有servers,然后swagger会创建一个默认的servers,这个默认的就是网关的ip+端口。
例图3

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

解决方案

那么这样就可以创建一个完整包路径与它一样的包,自己创建一个名字一样的类来覆盖框架中的类,然后在创建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;
    }
    }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

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

效果

然后打开/swagger-ui/页面,就发现服务名已经添加成功了。
例图7
尝试请求一个接口,路径也正确。
例图8
至此,问题解决。

我的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<SwaggerResource> get() {
      List<SwaggerResource> resources = new ArrayList<>();
      List<String> 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<String> 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;
      }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

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;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

其它微服务中的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()
    )
    );
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

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;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值