微服务与中间件系列——GateWay整合Swagger3增强Knife4j
- GateWay整合Swagger3增强Knife4j(easy模式)
- 目的
- 服务端
- 1.导入依赖
- 2.编写配置类
- 3.yaml配置
- GateWay网关
- 1.文档枚举
- 2.SwaggerProvider
- 3.yaml配置
- 结果
- 增强版
- 服务端
- 1.增加配置参数类
- 2.修改配置类
- 3.修改yaml配置
- GateWay网关
- 1.增加全局认证过滤器
GateWay整合Swagger3增强Knife4j(easy模式)
目的
统一进行文档管理,查看,避免swagger文档在微服务中使用繁琐的问题
本案例使用nacos作为服务的注册中心
服务端
1.导入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.编写配置类
package cn.fly.client.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
/**
* @author Syf200208161018
* @date 2022/11/19 15:44
* @ClassName:SwaggerConfig
* @Effect:SwaggerConfig is used for
*/
@Configuration
@EnableOpenApi
@Data
@ConfigurationProperties(prefix = "swagger")
public class SwaggerConfig {
/**
* 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
*/
private Boolean enable;
/**
* 项目应用名
*/
private String applicationName;
/**
* 项目版本信息
*/
private String applicationVersion;
/**
* 项目描述信息
*/
private String applicationDescription;
@Bean
public Docket docket() {
return new Docket(DocumentationType.OAS_30)
.pathMapping("/")
// 定义是否开启swagger,false为关闭,可以通过变量控制,线上关闭
.enable(enable)
//配置api文档元信息
.apiInfo(apiInfo())
// 选择哪些接口作为swagger的doc发布
.select()
//apis() 控制哪些接口暴露给swagger,
// RequestHandlerSelectors.any() 所有都暴露
// RequestHandlerSelectors.basePackage("net.xdclass.*") 指定包位置
// withMethodAnnotation(ApiOperation.class)标记有这个注解 ApiOperation
.apis(RequestHandlerSelectors.basePackage("cn.fly.client.controller.common"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(applicationName)
.description(applicationDescription)
.contact(new Contact("syf", "syf20020816@outlook.com", "syf20020816@outlook.com"))
.version(applicationVersion)
.build();
}
}
3.yaml配置
- 配置服务名称
- 配置mvc路径策略
- 配置nacos路径
- 配置swagger(自定义非官方,看配置类就知道了)
spring:
application:
name: Eprop-client
mvc:
pathmatch:
matching-strategy: ant_path_matcher
cloud:
nacos:
# server-addr: 192.168.31.149:8848
server-addr: 172.16.216.47:8848
swagger:
enable: true
application-name: Eprop-client
application-version: 1.0.0
application-description: client
GateWay网关
1.文档枚举
package cn.fly.gateway.swagger;
import io.netty.channel.nio.AbstractNioByteChannel;
/**
* @author Syf200208161018
* @date 2022/11/19 19:12
* @ClassName:DocEnum
* @Effect:DocEnum is used for
*/
public enum DocEnum {
/**
* 路由信息
*/
EPROP_COMMON("Eprop-client","eprop-API模块");
private String routeId;
private String swaggerInfo;
DocEnum(String routeId, String swaggerInfo) {
this.routeId = routeId;
this.swaggerInfo = swaggerInfo;
}
public String getRouteId() {
return routeId;
}
public void setRouteId(String routeId) {
this.routeId = routeId;
}
public String getSwaggerInfo() {
return swaggerInfo;
}
public void setSwaggerInfo(String swaggerInfo) {
this.swaggerInfo = swaggerInfo;
}
public static String getSwaggerInfoByRouteId(String routeId){
for (DocEnum value : DocEnum.values()) {
if (value.getRouteId().equals(routeId)){
return value.getSwaggerInfo();
}
}
return null;
}
}
2.SwaggerProvider
实现SwaggerResourcesProvider
对资源进行修改
package cn.fly.gateway.swagger;
import com.alibaba.cloud.commons.lang.StringUtils;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
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 Syf200208161018
* @date 2022/11/19 16:55
* @ClassName:SwaggerProvider
* @Effect:SwaggerProvider is used for
*/
/**
* 聚合系统接口
* @author ROCKY
*/
@Component
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v3/api-docs";
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
public SwaggerProvider(RouteLocator routeLocator, GatewayProperties gatewayProperties) {
this.routeLocator = routeLocator;
this.gatewayProperties = gatewayProperties;
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> 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()
// 目前只处理Path断言 Header或其他路由需要另行扩展
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> {
String routeId = routeDefinition.getId();
String swaggerInfo = DocEnum.getSwaggerInfoByRouteId(routeId);
if (StringUtils.isNotEmpty(swaggerInfo)) {
resources.add(swaggerResource(swaggerInfo, 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");
return swaggerResource;
}
}
3.yaml配置
-
注意路由的ID要对应服务的名称
-
注意过滤器的
StripPrefix=3
一定要和你前面指定的断言- Path=/api/v1/eprop/**
匹配上,比如我这个就是去除3个前缀,如果是/api/hello/**,就是去除2个前缀,这个一定不能错,因为这和swagger请求地址有关
server:
port: 21301
spring:
application:
name: gateway
cloud:
nacos:
# server-addr: 192.168.31.149:8848
server-addr: 172.16.216.47:8848
# server-addr: 192.168.1.103:8848
gateway:
routes:
- id: Eprop-client
uri: lb://Eprop-client
predicates:
- Path=/api/v1/eprop/**
filters:
- StripPrefix=3
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: # 允许哪些网站进行跨域请求
- "http://localhost:8080"
allowedMethods:
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许所有请求头
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 跨域监测有效期
add-to-simple-url-handler-mapping: true #解决options请求拦截
discovery:
locator:
lower-case-service-id: true
结果
启动gateway浏览器访问ip:端口/doc.html
成功如下:
增强版
其实也就是做了一下对于配置的处理
服务端
1.增加配置参数类
package cn.fly.client.properties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* @author Syf200208161018
* @date 2022/11/20 0:44
* @ClassName:SwaggerProperties
* @Effect:SwaggerProperties is used for
*/
@ConfigurationProperties(prefix = "swagger-api")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SwaggerProperties {
private String title="EPROP-API文档";
private String groupName = "default";
private String des="default API doc";
private String version="1.1.0";
// private Contact contact = new Contact("syf","syf20020816@outlook.com","syf20020816@outlook.com");
private String docMaker = "syf";
private String docUrl = "syf20020816@Outlook.com";
private String makerEmail="syf20020816@Outlook.com";
private String basePackage="cn.fly.client.controller";
//解析的url规则
private List<String> basePath = new ArrayList<>();
//需要排除的url
private List<String> excludePath = new ArrayList<>();
/**
* 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
*/
private Boolean enable;
/**
* 项目应用名
*/
private String applicationName;
/**
* 项目版本信息
*/
private String applicationVersion;
/**
* 项目描述信息
*/
private String applicationDescription;
}
2.修改配置类
package cn.fly.client.config;
import cn.fly.client.properties.SwaggerProperties;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
/**
* @author Syf200208161018
* @date 2022/11/19 15:44
* @ClassName:SwaggerConfig
* @Effect:SwaggerConfig is used for
*/
@Configuration
@EnableOpenApi
@EnableConfigurationProperties(SwaggerProperties.class)
public class SwaggerConfig {
@Autowired
private SwaggerProperties swaggerProperties;
@Bean
public Docket docket() {
return new Docket(DocumentationType.OAS_30)
.pathMapping("/")
// 定义是否开启swagger,false为关闭,可以通过变量控制,线上关闭
.enable(swaggerProperties.getEnable())
//配置api文档元信息
.apiInfo(apiInfo())
// 选择哪些接口作为swagger的doc发布
.select()
//apis() 控制哪些接口暴露给swagger,
// RequestHandlerSelectors.any() 所有都暴露
// RequestHandlerSelectors.basePackage("net.xdclass.*") 指定包位置
// withMethodAnnotation(ApiOperation.class)标记有这个注解 ApiOperation
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getApplicationDescription())
.contact(new Contact(swaggerProperties.getDocMaker(),swaggerProperties.getDocUrl(),swaggerProperties.getMakerEmail()))
.version(swaggerProperties.getApplicationVersion())
.build();
}
}
3.修改yaml配置
swagger-api:
enable: true
application-name: Eprop-client
application-version: 1.0.0
application-description: Eprop API文档Swagger3版本
title: EPROP-API 文档
groupName: Eprop-client
des: Eprop API文档Swagger3版本
version: 1.0.0
docMaker: syf
docUrl: www.eprop.fly.cn
markerEmail: syf20020816@Outlook.com
basePackage: cn.fly.client.controller
GateWay网关
1.增加全局认证过滤器
package cn.fly.gateway.filter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
/**
* @author Syf200208161018
* @date 2022/10/28 13:16
* @ClassName:AuthorizeFilter
* @Effect:AuthorizeFilter is used for filtering user
* the request header must have a token-eprop and then getUserInfo
*/
@Component
@Order(-1)
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println(exchange.getRequest().getURI().getPath());
if (exchange.getRequest().getURI().getPath().equals("/v3/api-docs")){
return chain.filter(exchange);
}else{
//get request params
final ServerHttpRequest request = exchange.getRequest();
//get header: token-eprop
final HttpHeaders requestHeaders = request.getHeaders();
final List<String> values = requestHeaders.get("token-eprop");
for (String value : values) {
System.out.println(value);
}
final String tokenValue = values.get(0);
System.out.println(tokenValue);
//if tokenValue is empty or null intercept the request
if (StringUtils.isBlank(tokenValue)){
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
}
其他无需更改