SpringCloudGateway整合swagger3文档

1 说明

         SpringCloud项目中,微服务模块和网关模块必不可少。按照以前SpringBoot的模式,单个服务拥有自己的Api文档(Swagger文档),引入微服务后,多文档管理成了一个问题。我们需要一个统一的入口方便前端同学查看。本篇文章就是把各个微服务的swagger-api文档,集成到网关服务下面。

SpringCloudGateway整合swagger3文档_第1张图片

        关于swagger3介绍,可见文章: https://mp.csdn.net/mp_blog/creation/editor/127736281https://mp.csdn.net/mp_blog/creation/editor/127736281        关于SpringCloudGateway介绍,可见文章:

Spring Cloud Gateway 服务网关的部署与使用详细介绍_张维鹏的博客-CSDN博客网关作为系统的唯一流量入口,封装内部系统的架构,所有请求都先经过网关,由网关将请求路由到合适的微服务,所以,使用网关的好处在于:(1)简化客户端的工作。网关将微服务封装起来后,客户端只需同网关交互,而不必调用各个不同服务;(2)降低函数间的耦合度。 一旦服务接口修改,只需修改网关的路由策略,不必修改每个调用该函数的客户端,从而减少了程序间的耦合性(3)解放开发人员把精力专注于业务逻辑的实现。由网关统一实现服务路由(灰度与ABTest)、负载均衡、访问控制、流控熔断降级等非业务相关功能https://blog.csdn.net/a745233700/article/details/122917167

2 代码部分

2.1 微服务部分

        假设我们已经有3个微服务了,对应3个api文档,分别是:

Swagger文档地址:

服务1:http://localhost:9001/java/swagger-ui/index.html

服务2:http://localhost:9100/files/swagger-ui/index.html

服务3:http://localhost:9200/pays/swagger-ui/index.html

对应的API接口如下:

服务1:http://localhost:9001/java/v3/api-docs?group=YX

服务2:http://localhost:9100/files/v3/api-docs?group=YX

服务3:http://localhost:9200/pays/v3/api-docs?group=YX

SpringCloudGateway整合swagger3文档_第2张图片

2.2 网关部分

        pom.xml:引入网关和swagger3




  4.0.0

  
    ssm-mult-module-demo
    org.example
    1.0-SNAPSHOT
  

  org.example
  module-cloud-gateway
  1.0-SNAPSHOT
  jar

  module-cloud-gateway Maven Webapp
  
  http://www.example.com

  
    UTF-8
    1.8
    1.8
  

  

    
      org.springframework.boot
      spring-boot-starter
    


    
    
      com.alibaba.cloud
      spring-cloud-starter-alibaba-sentinel
      ${spring-cloud-alibaba.version}
    
    
      com.alibaba.cloud
      spring-cloud-alibaba-sentinel-gateway
      ${spring-cloud-alibaba.version}
    

    
    
    
      org.springframework.cloud
      spring-cloud-starter-gateway
    


    
    
      io.springfox
      springfox-boot-starter
      ${swagger3.version}
    
  

  

  

  
    module-cloud-gateway

    
      
        
        org.springframework.boot
        spring-boot-maven-plugin
      
    
  


          application.yml:配置网关和转发路由

server:
  port: 8080
spring:
  application:
    name: module-cloud-gateway-win
  cloud:
    gateway: #网关路由配置
      httpclient:
        pool:
          max-idle-time: 10000
      routes:
        # 请求:http://localhost:8080/java/remote/user/test1 会转发到 http://localhost:9001/java/remote/user/test1
        - id: java-service   # 路由 id,没有固定规则,但唯一,建议与服务名对应
          uri: http://localhost:9001       # 匹配后提供服务的路由地址
          # 以下是断言条件,必选全部符合条件
          predicates:
            - Path=/java/**          #断言,路径匹配 注意:Path 中 P 为大写
        - id: files-service
          uri: http://localhost:9100/
          predicates:
            - Path=/files/**
        - id: pays-service
          uri: http://localhost:9200/
          predicates:
            - Path=/pays/**
springfox:
  documentation:
    swagger-ui:
      enabled: true
Swagger3Config.java:swagger配置
package com.module.nacos.file.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.*;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;
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.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.*;

/***
 * @author 
 * @date 
 * @apiNote 访问链接:http://localhost:8080/swagger-ui/index.html#/
 */
@EnableOpenApi
@Configuration
public class Swagger3Config {

    @Value("${spring.application.name}")
    private String PROJECT_NAME;

    /**
     * 配置基本信息
     * @return
     */
    @Bean
    public ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(PROJECT_NAME)
                .description("swagger test app restful api")
                .termsOfServiceUrl("http://localhost")
                .contact(new Contact("SSM", "http://localhost", "[email protected]"))
                .version("1.0")
                .build();
    }

    /**
     * 配置文档生成最佳实践
     *
     * @param apiInfo
     * @return
     */
    @Bean
    public Docket createRestApi(ApiInfo apiInfo) {
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo)
                .groupName("YX")
                .select()
                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
                .paths(PathSelectors.any())
                .build()
                //添加token的参数
                .securityContexts(securityContexts())
                .securitySchemes(securitySchemes());

    }

    /* ↓↓↓↓ 解决swagger3.0 head传参失效的问题 ↓↓↓↓ */
    private List securitySchemes() {
        List securitySchemes = new ArrayList<>();
        securitySchemes.add(new ApiKey("Authorization", "Authorization", "header"));
        securitySchemes.add(new ApiKey("Accept-Language", "Accept-Language", "header"));
        return securitySchemes;
    }

    private List securityContexts() {
        List securityContexts = new ArrayList<>();
        securityContexts.add(SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex("^(?!auth).*$")).build());
        return securityContexts;
    }

    private List defaultAuth() {
        AuthorizationScope authorizationScope1 = new AuthorizationScope("global", "token");
        AuthorizationScope[] authorizationScopes1 = new AuthorizationScope[1];
        authorizationScopes1[0] = authorizationScope1;

        AuthorizationScope authorizationScope2 = new AuthorizationScope("global", "language");
        AuthorizationScope[] authorizationScopes2 = new AuthorizationScope[1];
        authorizationScopes2[0] = authorizationScope2;

        List securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference("Authorization", authorizationScopes1));
        securityReferences.add(new SecurityReference("Accept-Language", authorizationScopes2));
        return securityReferences;
    }
    /* ↑↑↑↑ 解决swagger3.0 head传参失效的问题 ↑↑↑↑ */

    @SafeVarargs
    private final  Set hashSet(T... ts) {
        if (ts.length > 0) {
            return new LinkedHashSet<>(Arrays.asList(ts));
        }
        return null;
    }

    /**
     * 增加如下配置可解决Spring Boot 6.x 与Swagger 3.0.0 不兼容问题
     **/
    @Bean
    public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {
        List> allEndpoints = new ArrayList();
        Collection webEndpoints = webEndpointsSupplier.getEndpoints();
        allEndpoints.addAll(webEndpoints);
        allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
        allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
        String basePath = webEndpointProperties.getBasePath();
        EndpointMapping endpointMapping = new EndpointMapping(basePath);
        boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
        return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
    }

    private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
        return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
    }
}

SwaggerProvider.java:关键代码,注意API_URI参数
package module.cloud.gateway.config.swagger;

import lombok.AllArgsConstructor;
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 ssm
 * @version V1.0.4
 * @description TODO
 * @date 2023/2/27 17:55
 */
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {
    public static final String API_URI = "/v3/api-docs?group=YX";

    private final RouteLocator routeLocator;

    private final GatewayProperties gatewayProperties;

    /**
     * 这个类是核心,这个类封装的是SwaggerResource,即在swagger-ui.html页面中顶部的选择框,选择服务的swagger页面内容。
     * RouteLocator:获取spring cloud gateway中注册的路由
     * RouteDefinitionLocator:获取spring cloud gateway路由的详细信息
     * RestTemplate:获取各个配置有swagger的服务的swagger-resources
     */
    @Override
    public List get() {
        List resources = new ArrayList<>();
        List 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()
                        .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                        .forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
                                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.0");
        return swaggerResource;
    }
}

3 验证

        打开网关服务的Swagger链接:http://localhost:8080/swagger-ui/index.html

        右上角可切换不同服务

SpringCloudGateway整合swagger3文档_第3张图片

4 参考链接

(1) Spring Cloud Gateway 服务网关的部署与使用详细介绍_张维鹏的博客-CSDN博客

(2) SpringCloudAlibaba篇(八)SpringCloudGateWay聚合swagger3、SpringBoot2.6.X整合swagger3+knife4j_springboot 2.6整合springcloud gateway_fate急速出击的博客-CSDN博客

你可能感兴趣的:(代码解析,java,微服务,spring,cloud)