Knife4j 集成方案
方案选择
knife4j-micro-spring-boot-starter 和 knife4j-spring-boot-starter
采用聚合方案,选择使用这两个依赖包。Knife4jAggregation 聚合方案
由于官方文档明确指出其无法与 Spring Cloud Gateway 混合使用,因此放弃该方案。
参考文档:Knife4jAggregation 介绍knife4j-gateway-spring-boot-starter
由于当前 Spring Cloud 版本较低,存在兼容性问题,因此放弃该方案。
参考文档:Knife4j Gateway 实战文档
使用的依赖包
- knife4j-micro-spring-boot-starter
- knife4j-spring-boot-starter
版本均为3.0.3
。两者的主要区别在于knife4j-spring-boot-starter
额外引入了knife4j-spring-ui
(具体可查看依赖关系)。
实现目标
业务模块
引入knife4j-micro-spring-boot-starter
以生成 Swagger JSON 文档。Gateway 模块
引入knife4j-spring-boot-starter
以聚合文档。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>
核心代码
实现以下三个类:
- 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)));
}
}
- 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);
};
}
}
- 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;
}
}
验证步骤
启动 Gateway 和业务模块
访问业务模块
Swagger JSON 文档正常生成。
访问 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-mappings
为 false
,所有映射关系将失效,此时必须重写 addResourceHandlers
方法。