spring boot版本 3.3.4,jdk 22, springcloud 2023.0.3
官方参考链接
Spring Cloud Gateway网关聚合 | Knife4j (xiaominfo.com)
springboot版本信息
<properties>
<java.version>22</java.version>
<spring-cloud.version>2023.0.3</spring-cloud.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/>
</parent>
一、网关集成 knife4j 4.4.0
<dependency> 这是网关的maven坐标,它不用spring boot自带的tomcat
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
网关的核心配置
server:
port: 9101
servlet:
context-path: /
spring:
main:
web-application-type: reactive #不使用tomcat的配置,与gateway无关
application:
name: GatewayApp
cloud:
gateway:
#保证通过网关能访问子服务的配置 这个与gateway无关,主要是fegin调用使用
routes:
- id: ServiceApp9100 #你的子服务
uri: http://localhost:9100
predicates:
- Path=/service/** # service子服务的context-path值
- id: PortalApp9099 #你的子服务
uri: http://localhost:9099
predicates:
- Path=/protal/**
eureka: # 配置确认通过网关可以从eureka调用到子服务
instance:
lease-expiration-duration-in-seconds: 30
lease-renewal-interval-in-seconds: 10
hostname: localhost #服务主机名称
prefer-ip-address: true #是否优先使用ip来作为主机名
client:
register-with-eureka: true #注册到Eureka的注册中心
fetch-registry: true #获取注册实例列表
enabled: true # 启用eureka客户端
registry-fetch-interval-seconds: 30 #定义去eureka服务端获取服务列表的时间间隔
healthcheck:
enabled: true
service-url:
defaultZone: http://localhost:9098/eureka/ #eureka服务的地址
knife4j: #这里是重点
gateway:
# 开起网关聚合文档
enabled: true
# 网关前缀(如nginx配置的代理前缀) 默认:/
api-path-prefix: /
# 使用的UI版本(v2或者v3) 默认: v3
version: v3
# 指定服务发现的模式聚合微服务文档,并且是默认 default 分组
strategy: discover
# 服务发现
discover:
# OpenAPI 3.0 规范
version: openapi3
# 开启服务发现 默认:true
enabled: true
# 默认排序 默认:0
default-order: 0
# 排除的服务名 默认:为空(建议排除网关服务)
excluded-services: ${spring.application.name}
routes:
# 子服务分组名称
#子服务的服务名跟你的子服务名字一样就行 一般用servlet.context-path的值
- name: service
#这里很重要 必须有【service】,否则访问的是ip:port/v3/apidocs,报404
#官网在这里根本没有解释,估计官网写文档的认为开发都是高手,没有多余的交代
#service漏写很痛苦,各种尝试,各种jar包替换,没有过多的交代,导致开发者填坑
url: 'service/v3/api-docs'
# context-path
context-path: '/service'
# 服务名
service-name: service
# 排序
order: 1
上面的配置完,就能启动网关服务访问,前提是子服务也得集成结束(参见下文 “二、子服务集成”),要不然会报错
http://127.0.0.1:9101/doc.html 网关服务的访问地址/doc.html
高版本就是引入maven坐标,添加配置,启动项目就行,不用做任何配置类操作。
补充一点关于上面的访问链接 浏览器控制台404问题
源代码中 这个访问路径
knife4j:
gateway:
enabled: true #配置文件有这个就行
新增了两个maven坐标,原因是网关gateway排除了springboot自带的Tomcat包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
网关增加的配置类,目的是在网关访问时处理一下访问的路径,主要原因是它可能出现了
http://ip:port//xxx /这里是空/ 这种情况导致404,启动成功能正常访问,可以将这个配置类伤处试试,莫名其妙的也正常了。
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.properties.SwaggerUiConfigParameters;
import org.springdoc.core.properties.SwaggerUiConfigProperties;
import org.springdoc.core.properties.SwaggerUiOAuthProperties;
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springdoc.webmvc.core.configuration.SpringDocWebMvcConfiguration;
import org.springdoc.webmvc.ui.SwaggerIndexPageTransformer;
import org.springdoc.webmvc.ui.SwaggerWelcomeCommon;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.resource.ResourceTransformerChain;
import java.io.IOException;
import java.util.Optional;
@Configuration
@ConditionalOnClass(SpringDocWebMvcConfiguration.class)
public class SwaggerConfiguration {
/**
* 重写swagger地址,通过浏览器访问/swagger-ui/index.html时,html页面会请求/v3/api-docs和/v3/api-docs/swagger-config
* 1)本地(localhost)访问时,能正常返回
* 2)会出现404,因为访问的地址少了服务名:
* 比如应该是“http://host:port/服务名/v3/api-docs”,但实际访问的是“http://host:port//v3/api-docs”
*/
public class RewritePathSwaggerIndexPageTransformer extends SwaggerIndexPageTransformer {
private SpringDocConfigProperties springDocConfigProperties;
private SwaggerUiConfigParameters swaggerUiConfigParameters;
/**
* 上一次请求获取到的前缀
*/
private String lastPrefix = Strings.EMPTY;
public RewritePathSwaggerIndexPageTransformer(SwaggerUiConfigProperties swaggerUiConfig,
SwaggerUiOAuthProperties swaggerUiOAuthProperties, SwaggerUiConfigParameters swaggerUiConfigParameters,
SwaggerWelcomeCommon swaggerWelcomeCommon, ObjectMapperProvider objectMapperProvider,
SpringDocConfigProperties springDocConfigProperties) {
super(swaggerUiConfig, swaggerUiOAuthProperties, swaggerUiConfigParameters, swaggerWelcomeCommon,
objectMapperProvider);
this.springDocConfigProperties = springDocConfigProperties;
this.swaggerUiConfigParameters = swaggerUiConfigParameters;
}
@Override
public Resource transform(HttpServletRequest request, Resource resource,
ResourceTransformerChain transformerChain) throws IOException {
SpringDocConfigProperties.ApiDocs apiDocs = springDocConfigProperties.getApiDocs();
String apiDocsPath = apiDocs.getPath();
String configUrl = swaggerUiConfigParameters.getConfigUrl();
String prefix = getPrefix(request);
// 考虑同一个服务,可能有时候会被“走网关访问”,有时候会被“走IP+端口直接访问”,所以这里做了区分判断
// 通过网关转发
if (StringUtils.isNotBlank(prefix)) {
// 上一次不是通过网关转发,即需要重新调整;如果上一次是通过网关转发,则不能调整,否则就叠加两次了
if (!StringUtils.equals(lastPrefix, prefix)) {
String newApiDocsPath = prefix + apiDocsPath;
apiDocs.setPath(newApiDocsPath);
String newConfigUrl = RegExUtils.replaceFirst(configUrl, apiDocsPath, newApiDocsPath);
swaggerUiConfigParameters.setConfigUrl(newConfigUrl);
lastPrefix = prefix;
}
} else {
// 不是通过网关转发
// 上一次是通过网关转发,即需要重新调整
if (!StringUtils.equals(lastPrefix, prefix)) {
// 去掉转发时,网关添加的路径
String newApiDocsPath = RegExUtils.replaceFirst(apiDocsPath, lastPrefix, "");
apiDocs.setPath(newApiDocsPath);
String newConfigUrl = RegExUtils.replaceFirst(configUrl, apiDocsPath, newApiDocsPath);
swaggerUiConfigParameters.setConfigUrl(newConfigUrl);
lastPrefix = prefix;
}
}
return super.transform(request, resource, transformerChain);
}
/**
* 获取网关转发时,url上添加的前缀
*/
public String getPrefix(HttpServletRequest request) {
// 获取网关转发的服务名
return Optional.ofNullable(request.getHeader("x-forwarded-prefix")).orElse(Strings.EMPTY);
}
}
}
二、子服务集成
前提是子服务已经和eureka作好集成,通过网关能访问到子服务的api
maven坐标
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
配置文件中的配置,可能有些多余的,目前没有排除
server:
port: 9100
servlet:
context-path: /service
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs
group-configs:
- group: 'service'
paths-to-match: '/**'
packages-to-scan: 'com.xxx.xxxx' #你的包路径,比如com.xxx.xxx.controller
# knife4j的增强配置,不需要增强可以不配
knife4j:
enable: true
setting:
language: zh-CN
启动子服务就行,确保子服务自己的knife4j能访问到
至此集成完成。不需要任何配置类编写,就是引入maven坐标,整理好配置文件,启动类头上不需要任何关于knife4j的注解。
注意:如果eureka有Security管理,记得放开下面路径,没有引入Security应该不用管
/**
* 配置要忽略的路径 这个在eureka项目的启动类中加就行
*/
@Bean
WebSecurityCustomizer webSecurityCustomizer() {
// 忽略 /error /eureka/**
return web -> web.ignoring().requestMatchers("/error","/eureka/**",
"/swagger-ui.html","/webjars/**","/swagger-resources/**",
"/v2/*","/doc.html",
"/service/v3/api-docs","/v3/api-docs/swagger-config");
}