Spring Cloud Gateway integra la documentación de la interfaz de agregación de knife4j

        Cuando haya más y más microservicios en el sistema, si se permite que estos servicios se dispersen por todas partes, será muy problemático administrar los documentos de interfaz de cada proyecto, solo recuerde que el acceso a los documentos de interfaz de todos los microservicios es una tarea. Cuando los documentos de interfaz de todos los proyectos de microservicio se puedan unificar en la misma página visual, nuestro trabajo de gestión y mantenimiento de documentos de interfaz se reducirá considerablemente. Por esta razón, podemos usar Spring Cloud Gateway gateway + nacos + knife4j para todos los microservicios. los documentos de interfaz del proyecto se agregan para realizar la función de gestión de documentos que queremos

Nota: este caso requiere que springboot integre nacos como el registro de antemano, y springcloud para integrar parte del registro de nacos.Bienvenido a leer este artículo: Introducción detallada a la implementación y el uso del registro de nacos

1. La puerta de enlace Spring Cloud Gateway integra Knife4j:

(1) Encienda la función de enrutamiento automático de la puerta de enlace:

        Con el desarrollo continuo de la arquitectura de nuestro sistema, la cantidad de microservicios en el sistema definitivamente aumentará. Es imposible para nosotros configurar una nueva regla de enrutamiento en la puerta de enlace cada vez que agregamos un servicio. Dichos costos de mantenimiento son muy altos, especialmente en En muchos casos, llevaremos un identificador de enrutamiento en la ruta de la solicitud para facilitar el reenvío, y este identificador de enrutamiento generalmente es el nombre del servicio en el registro, por lo que aquí es donde podemos habilitar la función de enrutamiento automático de Spring Cloud Gateway. puerta de enlace Cree automáticamente un enrutador para cada servicio en función del nombre del servicio del registro y reenvíe la ruta de solicitud que comienza con el nombre del servicio al servicio correspondiente. La configuración es la siguiente:

# enabled:默认为false,设置为true表明spring cloud gateway开启服务发现和路由的功能,网关自动根据注册中心的服务名为每个服务创建一个router,将以服务名开头的请求路径转发到对应的服务
spring.cloud.gateway.discovery.locator.enabled = true
# lowerCaseServiceId:启动 locator.enabled=true 自动路由时,路由的路径默认会使用大写ID,若想要使用小写ID,可将lowerCaseServiceId设置为true
spring.cloud.gateway.discovery.locator.lower-case-service-id = true

        Cabe señalar aquí que si configuramos la propiedad server.servlet.context-path, esto causará el problema de falla de enrutamiento automático, por lo que debemos realizar las siguientes dos modificaciones:

# 重写过滤链,解决项目设置了 server.servlet.context-path 导致 locator.enabled=true 默认路由策略404的问题
spring.cloud.gateway.discovery.locator.filters[0] = PreserveHostHeader
@Configuration
public class GatewayConfig
{
    @Value ("${server.servlet.context-path}")
    private String prefix;

    /**
     * 过滤 server.servlet.context-path 属性配置的项目路径,防止对后续路由策略产生影响,因为 gateway 网关不支持 servlet
     */
    @Bean
    @Order (-1)
    public WebFilter apiPrefixFilter()
    {
        return (exchange, chain) ->
        {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getRawPath();

            path = path.startsWith(prefix) ? path.replaceFirst(prefix, "") : path;
            ServerHttpRequest newRequest = request.mutate().path(path).build();

            return chain.filter(exchange.mutate().request(newRequest).build());
        };
    }
}

        En este punto, la puerta de enlace creará automáticamente un enrutador para cada servicio según el nombre del servicio del registro y reenviará la ruta de solicitud que comienza con el nombre del servicio al servicio correspondiente.

(2) El archivo pom.xml introduce dependencias de knife4j:

		<dependency>
			<groupId>com.github.xiaoymin</groupId>
			<artifactId>knife4j-spring-boot-starter</artifactId>
			<version>2.0.4</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
		</dependency>

(3)配置 SwaggerHeaderFilter:

        Al integrar la puerta de enlace de Spring Cloud Gateway, no habrá una ruta base, como el /usuario, /pedido y otros prefijos de microservicio definidos, por lo que debemos agregar un filtro de filtro a la puerta de enlace de la puerta de enlace.

import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;

@Configuration
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory
{
    private static final String HEADER_NAME = "X-Forwarded-Prefix";
    private static final String URI = "/v2/api-docs";

    @Override
    public GatewayFilter apply(Object config)
    {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();

            if(StringUtils.endsWithIgnoreCase(path, URI))
            {
                String basePath = path.substring(0, path.lastIndexOf(URI));
                ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
                ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
                return chain.filter(newExchange);
            }
            else
            {
                return chain.filter(exchange);
            }
        };
    }
}

(4) Vuelva a escribir swagger-resources:

        Cuando integramos Swagger con una sola arquitectura como SpringBoot, agrupamos las empresas en función de las rutas de los paquetes y luego mostramos diferentes módulos en el front-end.Bajo la arquitectura de microservicio, un servicio es similar a un grupo empresarial que escribimos originalmente. La interfaz de agrupación proporcionada por springfox-swagger es swagger-resource, que devuelve información como el nombre y la dirección de la interfaz de agrupación. En la arquitectura de microservicios de Spring Cloud, necesitamos reescribir esta interfaz y descubrir dinámicamente todos los datos a través del registro de la puerta de enlace. Documento de microservicio, el código es el siguiente:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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;

/**
 * 使用Spring Boot单体架构集成swagger时,是通过包路径进行业务分组,然后在前端进行不同模块的展示,而在微服务架构下,单个服务类似于原来业务组;
 * springfox-swagger提供的分组接口是swagger-resource,返回的是分组接口名称、地址等信息;
 * 在Spring Cloud微服务架构下,需要swagger-resource重写接口,由网关的注册中心动态发现所有的微服务文档
 */
@Primary
@Configuration
public class SwaggerResourceConfig implements SwaggerResourcesProvider
{
    @Autowired
    private RouteLocator routeLocator;
    
    // 网关应用名称
    @Value ("${spring.application.name}")
    private String applicationName;

    //接口地址
    private static final String API_URI = "/v2/api-docs";

    @Override
    public List<SwaggerResource> get() {
        //接口资源列表
        List<SwaggerResource> resources = new ArrayList<>();
        //服务名称列表
        List<String> routeHosts = new ArrayList<>();

        // 获取所有可用的应用名称
        routeLocator.getRoutes()
                .filter(route -> route.getUri().getHost() != null)
                .filter(route -> !applicationName.equals(route.getUri().getHost()))
                .subscribe(route -> routeHosts.add(route.getUri().getHost()));

        // 去重,多负载服务只添加一次
        Set<String> existsServer = new HashSet<>();
        routeHosts.forEach(host -> {
            // 拼接url
            String url = "/" + host + API_URI;
            //不存在则添加
            if (!existsServer.contains(url)) {
                existsServer.add(url);
                SwaggerResource swaggerResource = new SwaggerResource();
                swaggerResource.setUrl(url);
                swaggerResource.setName(host);
                resources.add(swaggerResource);
            }
        });
        return resources;
    }
}
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.*;

import java.util.Optional;

/**
 * 获取api接口信息
 */
@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));
    }

    @GetMapping("")
    public Mono<ResponseEntity> swaggerResources()
    {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}

2. Otros proyectos en la arquitectura de microservicios access knife4j:

<!-- knife4j文档,微服务架构 -->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-micro-spring-boot-starter</artifactId>
    <version>2.0.4</version>
</dependency>
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.async.DeferredResult;
import springfox.documentation.builders.*;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.paths.RelativePathProvider;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.servlet.ServletContext;

/**
 * @description: swagger配置文件
 **/
@Configuration
@EnableSwagger2
@EnableKnife4j
public class Swagger2Config
{
    @Value("${spring.profiles.active}")
    private String env;
    @Value("${spring.application.name}")
    private String serviceName;
    @Value("${gateway.service.name}")
    private String serviceNameForGateway;

    @Bean
    public Docket createDocket(ServletContext servletContext)
    {
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .genericModelSubstitutes(DeferredResult.class)
                .forCodeGeneration(true)
                .pathMapping("/")
                .select()
                .build()
                .apiInfo(new ApiInfoBuilder()
                        .title(serviceName + "接口文档")
                        .version("1.0")
                        .contact(new Contact("xxx","",""))
                        .license("XXX有限公司")
                        .build())
                // 如果为生产环境,则不创建swagger
                .enable(!"real".equals(env));

        // 在knife4j前端页面的地址路径中添加gateway网关的项目名,解决在调试接口、发送请求出现404的问题
        docket.pathProvider(new RelativePathProvider(servletContext)
        {
            @Override
            public String getApplicationBasePath()
            {
                return "/" + serviceNameForGateway + super.getApplicationBasePath();
            }
        });

        return docket;
    }
}

3. Efecto final:

Al final del artículo, presentaré otro método de agregación de documentos de interfaz proporcionado oficialmente por knife4j: Componente de agregación de microservicios de agregación, dirección oficial: https://doc.xiaominfo.com/knife4j/resources/ , los lectores interesados ​​pueden ir al oficial sitio web Ver cómo usar

Supongo que te gusta

Origin blog.csdn.net/a745233700/article/details/122917137
Recomendado
Clasificación