当前位置: 首页 > article >正文

通过Knife4j在gateway中对swagger文档进行聚合

Knife4j 集成方案

方案选择

  1. knife4j-micro-spring-boot-starterknife4j-spring-boot-starter
    采用聚合方案,选择使用这两个依赖包。

  2. Knife4jAggregation 聚合方案
    由于官方文档明确指出其无法与 Spring Cloud Gateway 混合使用,因此放弃该方案。
    参考文档:Knife4jAggregation 介绍

  3. knife4j-gateway-spring-boot-starter
    由于当前 Spring Cloud 版本较低,存在兼容性问题,因此放弃该方案。
    参考文档:Knife4j Gateway 实战文档

使用的依赖包

  1. knife4j-micro-spring-boot-starter
  2. knife4j-spring-boot-starter
    版本均为 3.0.3。两者的主要区别在于 knife4j-spring-boot-starter 额外引入了 knife4j-spring-ui(具体可查看依赖关系)。

实现目标

  1. 业务模块
    引入 knife4j-micro-spring-boot-starter 以生成 Swagger JSON 文档。

  2. Gateway 模块
    引入 knife4j-spring-boot-starter 以聚合文档。

  3. Gateway 页面展示
    需自行实现 swagger-resource 端点,确保页面正常显示。


代码实现

业务模块

引入依赖
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-micro-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>
代码说明

默认情况下,无需额外配置即可使用。若需开启增强模式,可参考后续章节。


Gateway 模块

引入依赖
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>
核心代码

实现以下三个类:

  1. SwaggerHandler
    处理 Swagger 资源请求。
package com.hatzi.gateway.swagger;

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.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;

import java.util.Optional;

@RestController
public class SwaggerHandler {

    private final SwaggerResourcesProvider swaggerResources;
    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;
    @Autowired(required = false)
    private UiConfiguration uiConfiguration;

    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }

    @GetMapping("/swagger-resources/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/swagger-resources/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/swagger-resources")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}
  1. SwaggerHeaderFilter
    拦截器,用于处理请求头。
package com.hatzi.gateway.swagger;

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

@Component
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)) {
                return chain.filter(exchange);
            }
            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);
        };
    }
}
  1. SwaggerResourceConfig
    配置 Swagger 资源。
package com.hatzi.gateway.swagger;

import com.hatzi.core.constant.SwaggerConstant;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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;

@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceConfig implements SwaggerResourcesProvider {

    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;

    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));

        gatewayProperties.getRoutes().stream()
                .filter(routeDefinition -> routes.contains(routeDefinition.getId()))
                .forEach(route -> {
                    route.getPredicates().stream()
                            .filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
                            .forEach(predicateDefinition -> {
                                String genKey = predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0");
                                String location = genKey.replace("**", "v2/api-docs");
                                resources.add(swaggerResource(route.getId(), location));
                            });
                });

        return resources;
    }

    private SwaggerResource swaggerResource(String name, String location) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion("1.0.0");
        return swaggerResource;
    }
}

验证步骤

  1. 启动 Gateway 和业务模块
    启动结果

  2. 访问业务模块
    Swagger JSON 文档正常生成。
    业务模块结果

  3. 访问 Gateway
    Gateway 页面正常显示。
    Gateway 结果


增强模式配置

若需使用增强模式(如文档访问密码、标题修改等),需在业务模块中配置增强模式。以下为示例代码:

package com.hatzi.swagger.config;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.hatzi.swagger.config.properties.SwaggerProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.RestController;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfiguration {

    @Bean
    public Docket defaultApi() {
        Contact contact = new Contact("作者", "联系url", "邮箱");
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo("接口文档", "接口文档", contact))
                .select()
                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo(String title, String description, Contact contact) {
        return new ApiInfoBuilder()
                .title(title)
                .description(description)
                .contact(contact)
                .build();
    }
}

常见问题

为什么某些博客提到必须重写 addResourceHandlers 才能使 doc.html 正常访问?
这是因为在 Spring Boot 中,若配置 spring.mvc.add-mappingsfalse,所有映射关系将失效,此时必须重写 addResourceHandlers 方法。
配置说明


http://www.kler.cn/a/595488.html

相关文章:

  • 流程优化的可配置文档结构化系统设计
  • 计算机网络精讲day1——计算机网络的性能指标(上)
  • 【人工智能】如何理解transformer中的token?
  • 【AI知识】pytorch手写Attention之Self-Attention,Multi-Head-Attention
  • vue3源码分析 -- computed
  • 深度解析学术论文成果评估(Artifact Evaluation):从历史到现状
  • 【问题解决】Postman 测试报错 406
  • 深入理解Java虚拟机(学习笔记)
  • java基础--序列化与反序列化的概念是什么?
  • 关于FastAPI框架的面试题及答案解析
  • 查看visual studio的MSVC版本的方法
  • 23 种设计模式中的访问者模式
  • 零基础上手Python数据分析 (7):Python 面向对象编程初步
  • 蓝桥杯 之 暴力回溯
  • 3.16[A]FPGA
  • Pytest基础使用
  • Netty源码—3.Reactor线程模型三
  • L2TP实验报告
  • 无服务器架构将淘汰运维?2025年云计算形态预测
  • RabbitMQ 与 Kafka:消息中间件的终极对比与选型指南