【SpringCloud】6.gateway——网关
前面学了openFeign(https://www.cnblogs.com/luyj00436/p/18588116),负责服务发现和负载均衡。
gateway : 网关 (对外)
openFeign 对内
SpringCloud Gateway本身也是一个微服务。需要注册服务中心进行注册。
概述
Spring Cloud gateway官网地址:https://docs.spring.io/spring-cloud-gateway/docs/4.0.4/reference/html/
为了统一管理微服务和安全,要使用网关。
Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;
但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul。
网关的作用:
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
Gateway的三大核心
Web前端请求,通过一些匹配条件,定位到真正的服务节点。在这个转发过程的前后,会进行一些精细化的控制。predicate就是我们的匹配条件;filter,可以理解为无所不能的拦截器。有了这两个元素,加上目标url,就可以实现具体的路由了。
- Route(路由),路由是构建网关的基本模块,它有ID,目标URL,一系列的断言和过滤器组成,如果断言为true,则匹配该路由。
- Predicate(断言)
- Filter(过滤),使用
GatewayFilter
过滤器,可以对请求前后进行修改。
后文的高级特性中,会更具体的讲到这三大核心的使用。
注:Gateway本身也是微服务
入门配置
建Mondule,建立一个子模块cloud-gateway9527
改POM, 为新建的子模块cloud-gateway9527
添加服务依赖(添加网关、consul)
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
写YML,将依赖注入consual
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
主启动:
@SpringBootApplication
@EnableDiscoveryClient //服务注册和发现
public class Main9527
{
public static void main(String[] args)
{
SpringApplication.run(Main9527.class,args);
}
}
测试
先启动8500服务中心Consul,在启动9527网关入驻。
网关做路由映射
目前,假设现在不想对外暴露8001端口。希望在8001端口外套一层9527。
例子
这里我们在8001端口对应的模块(cloud-provider-payment-8001)新增测试类:
@RestController
public class PayGateWayController {
@Autowired
PayServiceImpl payService;
@GetMapping(value = "/pay/gateway/get/{id}")
public ResultData<Pay> getPayId(@PathVariable("id") Integer id) {
return ResultData.success(payService.getById(id));
}
@GetMapping(value = "/pay/gateway/info")
public ResultData<String> getGatewayInfo(){
return ResultData.success("/gateway info:" + IdUtil.simpleUUID());
}
}
启动8001,测试自测通过。
网关9527,写YAML,新增8001的配置。
注:匹配路由的地址直接用端口号8001,并不利于维护。建议配合服务名,这里只是为了入门演示
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
启动8500/8001支付端口,网关9527端口后,可以通过:http://localhost:9527/pay/gateway/get/1 访问真实的接口: http://localhost:8001/pay/gateway/info
同一个公司,系统内环境,直接找微服务
不同公司,系统外访问,先网关再服务
GateWay高级特性
Router 动态微服务名称
前面的例子,URL地址写死,端口变更。好可怕😨。路由地址的格式可以:spring.cloud.gateway.routes.url:lb://service
,lb:// + 服务名。
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
此时,访问网关仍然能访问。修改被调用的服务端口,也不受影响。
断言(谓语)
启动网关后,查看后台:
断言有两种语法配置:Shortcut Configuration 和 Fully Expanded Arguments。后续例子使用的是Shortcut 配置。
接下来,我们可以依据官网,学习断言的使用,先看第一个例子,After。
这个时间格式?2017-01-20T17:42:47.789-07:00[America/Denver]
是什么?是包含时区的日期时间格式。可以使用代码ZonedDateTime.now();
获取。
常用断言api如下:
- After : 指定日期时间后发生请求
- Before :指定日期时间前发生请求
- Between: 指定日期时间之间发生请求
- Cookie: 包含Cookie断言
- Header : 包含头Header断言
- Host: host标头匹配断言
- Method: 方法断言,例如固定只能GET、POST请求
- Path: 路径断言,路由想匹配
- Query:查询断言,支持两个参数:属性名和属性值,属性值可以为正则表达式
- RemoteAddr:外部访问的IP限制
当然,如果常见断言不能满足我们的要求。我们可以进行自定义断言。
接下来,我们配置和使用断言。测试地址,为前文网关映射的正确地址测试:http://localhost:9527/pay/gateway/get/1
After 指定日期后发起请求
我们可以根据zonedDateTime ,获取日期时间格式。
写YAML,如下:
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- After=2024-12-30T10:28:00.231002700+08:00[Asia/Shanghai]
设置断言后,After参数的时间之前,请求返回404:
{
"timestamp": "2024-12-30T02:27:14.504+00:00",
"path": "/pay/gateway/get/1",
"status": 404,
"error": "Not Found",
"requestId": "f85bffd7-5"
}
After时间后,请求调用正确的微服务dia调用结果:
{
"code": "200",
"message": "success",
"data": {
"id": 1,
"payNo": "pay17203699",
"orderNo": "6544bafb424a",
"userId": 1,
"amount": 9.90,
"deleted": 0,
"createTime": "2024-11-26 15:13:02",
"updateTime": "2024-11-26 15:13:02"
},
"timestamp": 1735525896520
}
Before
同理,before表示时间前可以发起请求
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- Before=2024-12-30T10:36:00.231002700+08:00[Asia/Shanghai]
Between
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- Between=2024-12-30T10:36:00.231002700+08:00[Asia/Shanghai],2024-12-30T10:42:00.231002700+08:00[Asia/Shanghai]
包含Cookie的断言
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- Cookie=username,zzyy # 请求需包含cookie ,username,值zzyy(zzyy可以为正则表达式)
Header
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
Host
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- Host=**.atguigu.com
post请求测试时,头文件添加Host
参数:
Method
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- Method=GET,POST
Path
断言,以在前文中使用过
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
Query
- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
RemoteAddress
- RemoteAddr=192.168.124.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。
自定义断言
如果原有的断言不能满足我们的要求,我们可以使用自定义断言。自定义断言步骤如下:
- 新建类名XXX需要以RoutePRedicateFactory结尾
- 重写apply方法
- 新建apply方法所需要的静态内部类,MyRoutePredicateFactory.Config,这个Config类就是我们的路由断言规则。
- 空参构造方法,内部调用super
- 重写apply方法。
完整代码如下:
/**
* 自定义断言
* @Author:lyj
* @Date:2024/12/30 13:32
*/
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
@Validated
public static class Config {
@Setter
@Getter
@NotNull
private String userType; // 钻石、金、银等用户等级
}
public MyRoutePredicateFactory() {
super(MyRoutePredicateFactory.Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
// 检查request的参数里,userType是否为指定值,符合配置则通过
String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
if (userType == null) {
return false;
}
if (userType.equals(config.getUserType())) {
return true;
}
return false;
}
};
}
}
自定义配置
上述代码,缺少shortFieldOrder 方法的实现,所以不支持短格式。
spring:
cloud:
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- name: My
args:
userType: diamond
要想支持短格式,重写方法shortcutFieldOrder
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("userType");
}
Fileter 过滤器
官网地址:https://docs.spring.io/spring-cloud-gateway/docs/4.0.4/reference/html/#gatewayfilter-factories
"pre"和“post”分表会在请求被执行前调用和被执行后调用,用来修改请求和响应信息。
作用:请求鉴权和异常处理、
过滤器类型包括:
- 全局默认过滤器Global Filters
- 单一内置过滤器GatewayFilter
- 自定义过滤器
全局过滤器GlobalFilters
官网例子:https://docs.spring.io/spring-cloud-gateway/docs/4.0.4/reference/html/#gatewayfilter-factories。gateway出厂默认已有,直接用即可,主要用于所有的路由,不需要在配置文件中配置,只需要实现GlobalFilter
接口即可。
/**
* 自定义过滤器
* @Author:lyj
* @Date:2024/12/30 14:14
*/
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return -1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("【全局过滤器 MyGlobalFilter】");
return chain.filter(exchange);
}
}
添加自定义过滤器的配置
@Configuration
public class Myconfig {
@Bean
public GlobalFilter myGlobalFilter(){
return new MyGlobalFilter();
}
}
单一内置过滤器GatewayFilter
官网地址:https://docs.spring.io/spring-cloud-gateway/docs/4.0.4/reference/html/#gatewayfilter-factories
官网有37个过滤器.接下来,只讲常用和通用的内置过滤器:
过滤器分组 | 过滤器 |
---|---|
请求头(RequestHeader)相关组 | 6.1 The AddRequestHeader GatewayFilter Factory 6.18The RemoveRequestHeader GatewayFilter Factory 6.29The SetRequestHeader GatewayFilter Factory |
请求参数(RequestParameter)相关组 | 6.3The AddRequestParameter GatewayFilter Factory 6.19The RemoveRequestParameter GatewayFilter Factory |
回应头(ResponseHeader)相关组 | 6.4The AddResponseHeader GatewayFilter Factory 6.30The SetResponseHeader GatewayFilter Factory 6.20The RemoveResponseHeader GatewayFilter Factory |
前缀和路径相关组 | 6.14The PrefixPath GatewayFilter Factory 6.29The SetRequestHeader GatewayFilter Factory 6.16The RedirectTo GatewayFilter Factory |
其他 | 6.37 Default Filters |
测试过滤器前,被调用的微服务8001的PayGateWayController
新增方法如下:
@GetMapping(value = "/pay/gateway/filter")
public ResultData<String> getGatewayFilter(HttpServletRequest request)
{
String result = "";
Enumeration<String> headers = request.getHeaderNames();
while(headers.hasMoreElements())
{
String headName = headers.nextElement();
String headValue = request.getHeader(headName);
System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
if(headName.equalsIgnoreCase("X-Request-atguigu1")
|| headName.equalsIgnoreCase("X-Request-atguigu2")) {
result = result+headName + "\t " + headValue +" ";
}
}
return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
}
网关9527,设置如下:
spring:
cloud:
gateway:
routes:
- id: pay_routh3 # pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
测试地址:http://localhost:9527/pay/gateway/filter
请求头(RequestHeader)相关组
he AddRequestHeader GatewayFilter Factor,指定请求头ByName
spring:
cloud:
gateway:
routes:
- id: pay_routh3 # pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-atguigu2,atguiguValue2
the RemoveRequestHeader GatewayFilter Factory,移除请求头ByName
spring:
cloud:
gateway:
routes:
- id: pay_routh3 # pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
修改前请求头值:
修改后,请求头sec-fetch-site
被删除。
The SetRequestHeader GatewayFilter Factory,修改请求头ByName
- SetRequestHeader=sec-fetch-mode, Blue-updatebyzzyy # 将请求头sec-fetch-mode对应的值修改为Blue-updatebyzzyy
请求参数(RequestParamter)相关组
- AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
- RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
回应头(ResponseHeader)相关组
- AddResponseHeader=X-Response-atguigu, BlueResponse # 新增请求参数X-Response-atguigu并设值为BlueResponse
- SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
- RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
前缀和路径相关组
- PrefixPath=/pay # http://localhost:9527/pay/gateway/filter # 自动添加路径前缀
- SetPath=/pay/gateway/{segment} # 访问路径修改,{segment}表示占位符,你写abc也行但要上下一致
- RedirectTo=302, http://www.atguigu.com/ # 访问路径跳转,访问http://localhost:9527/pay/gateway/filter跳转到http://www.atguigu.com/
其他
将自定义过滤,变为Global。
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin
自定义过滤器
接下来,我们将根据实际场景,看看过滤器的使用情况——统计接口的耗时情况。
此时,我们想到了3个方法:
- 直接在代码前后修改👎
- 使用AOP
- 通过全局过滤器搞定需求,参考:https://docs.spring.io/spring-cloud-gateway/docs/4.0.4/reference/html/#gateway-combined-global-filter-and-gatewayfilter-ordering
直接修改接口代码,代码冗余度高,代码入侵性强,在接口中加入与业务无关的代码耦合性高,可读性差,首先排除。
使用AOP,之前也有学到,这里就不详细说明了。
自定义全局过滤器
写YAML,确定路径匹配被调用端,8001
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- After=2023-12-30T23:02:39.079979400+08:00[Asia/Shanghai]
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由,默认正确地址
filters:
- AddRequestHeader=X-Request-atguigu1,atguiguValue1 # 请求头kv,若一头含有多参则重写一行设置
写code,引入全局过滤器
/**
* @auther zzyy
* @create 2023-11-22 17:27
*/
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered
{
/**
* 数字越小优先级越高
* @return
*/
@Override
public int getOrder()
{
return 0;
}
private static final String BEGIN_VISIT_TIME = "begin_visit_time";//开始访问时间
/**
*第2版,各种统计
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//先记录下访问接口的开始时间
exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
return chain.filter(exchange).then(Mono.fromRunnable(()->{
Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
if (beginVisitTime != null){
log.info("访问接口主机: " + exchange.getRequest().getURI().getHost());
log.info("访问接口端口: " + exchange.getRequest().getURI().getPort());
log.info("访问接口URL: " + exchange.getRequest().getURI().getPath());
log.info("访问接口URL参数: " + exchange.getRequest().getURI().getRawQuery());
log.info("访问接口时长: " + (System.currentTimeMillis() - beginVisitTime) + "ms");
log.info("我是美丽分割线: ###################################################");
System.out.println();
}
}));
}
}
自定义单一过滤器
- 新建类名XXX需要以
GatewayFilterFactory
结尾,并集成AbstractGatewayFilterFactory
类。 - 新建
XXXGatewayFilterFactory.Config
内部类 - 重写
apply
方法 - 重写
shortcutFieldOrder
- 空参构造方法,内部调用super
* 自定义过滤
* @Author:lyj
* @Date:2024/12/31 10:09
*/
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
public static class Config {
@Getter
@Setter
private String status;
}
public MyGatewayFilterFactory() {
super(MyGatewayFilterFactory.Config.class);
}
@Override
public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
System.out.println("进入自定义网关过滤器MyGatewayFilterFactory,status===="+config.getStatus());
if(request.getQueryParams().containsKey("atguigu")) {
return chain.filter(exchange);
}else {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
}
};
}
@Override
public List<String> shortcutFieldOrder() {
List<String> list = new ArrayList<String>();
list.add("status");
return list;
}
}
自定义构造参数在spring.cloud.gateway.routes.-id.filters
下,名称为自定义过滤器XXXGatewayFilterFactory
前的XXX。