1、GatewayFilter Factories(路由过滤器)
官方访问地址:点击这里
来自官方的解释如下图所示:
简单来说就是:
- 客户端向 Spring Cloud Gateway 发送请求。
- 如果请求与某个路由匹配,则该请求会被传递给 Gateway Web Handler。
- Gateway Web Handler 将请求通过一系列过滤器,这些过滤器可以做一些预处理工作(比如添加头部信息、修改请求参数等)。
- 过滤器处理完成后,实际的代理请求才会被发送到目标服务。
- 当响应从目标服务返回时,它还会经过相同的过滤器链,这时过滤器可以做一些后处理工作(例如修改响应内容、添加响应头等)。
- 最终处理完毕后,响应被返回给客户端。
路由过滤器的主要作用就是用来做请求鉴权、异常处理等,比较典型的就有记录接口调用时长统计。
2、 Filter过滤的类型
- 全局默认过滤器Global Filters:官网;gateway出厂默认已有的,直接用即可,主要作用于所有的路由不需要在配置文件中配置,作用在所有的路由上,实现GlobalFilter接口即可;
- 单一内置过滤器GatewayFilter: 官网;也可以称为网关过滤器,这种过滤器主要是作用于单一路由或者某个路由分组;
- 定义过滤器
3、 Gateway内置的过滤器
这里只演示常用的内置过滤器,其他的原理都是一样的。
3.1 The AddRequestHeader GatewayFilter Factory(指定请求头内容ByName)
我们在网关controller中编写测试接口:
Example 14. GateWayController.java
@RestController
public class GateWayController{
@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-red")
|| headName.equalsIgnoreCase("X-Request-my")) {
result = result+headName + "\t " + headValue +" ";
}
}
return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
}
}
Example 14. application.yml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-red, blue # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-my,name
重启服务,并访问http://localhost:端口号/pay/gateway/filter,查看控制台信息。
3.2 The RemoveRequestHeader GatewayFilter Factory (删除请求头ByName)
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site
注意:观察删除前后控制台打印的信息
删除前:
删除后:
3.3 The SetPath GatewayFilter Factory(修改请求头)
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- SetRequestHeader=sec-fetch-mode,Blue-updatebyzzyy # 将请求头sec-fetch-mode对应的值修改为Blue-updatebyzzyy
修改前:
修改后(注意要重启服务):
3.4 The AddRequestParameter\RemoveRequestParameter GatewayFilter Factory(添加\删除请求参数)
spring:
cloud:
gateway:
routes:
- id: removerequestparameter_route
uri: https://example.org
filters:
- RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
- AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
修改controller中的接口方法:
@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-red")
|| headName.equalsIgnoreCase("X-Request-my")) {
result = result+headName + "\t " + headValue +" ";
}
}
System.out.println("=============================================");
String customerId = request.getParameter("customerId");
System.out.println("request Parameter customerId: "+customerId);
String customerName = request.getParameter("customerName");
System.out.println("request Parameter customerName: "+customerName);
System.out.println("=============================================");
return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
}
访问地址:
http://localhost:端口号/pay/gateway/filter
http://localhost:端口号/pay/gateway/filter?customerld=9999&customerName=z3
注意观察控制台打印信息。。
3.5 响应头相关的配置
- The AddResponseHeader GatewayFilter Factory
- The RemoveResponseHeader GatewayFilter Factory
- ** The SetRequestHeader GatewayFilter Factory**
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Red, Blue #新增响应头参数,并将X-Response-Red的值设置为Blue
- RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
- SetResponseHeader=Date,2099-11-11 # 设置响应头Date值为2099-11-11
3.6 前缀和路径相关的配置
- The PrefixPath GatewayFilter Factory(自动添加路径前缀)
- The SetPath GatewayFilter Factory(修改访问路径)
- The RedirectTo GatewayFilter Factory(重定向到某个页面)
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
# - Path=/gateway/filter/** # 真实地址
- Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤
- Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代
filters:
- PrefixPath=/mypath # 表示加了这个前缀胡访问的就是http://localhost:端口号/mypath/gateway/filter,,但是实际上调用的还是http://localhost:端口号/gateway/filter
- SetPath=/pay/gateway/{segment} # {segment}表示占位符,你写abc也行但要上下一致
- RedirectTo=302, http://www.baidu.com/ # 访问http://localhost:9527/gateway/filter跳转到http://www.baidu.com/
4、自定义全局Filter
需求:统计接口调用耗时情况。
通过自定义全局过滤器搞定上述需求。
4.1 实现步骤
1、新建类MyGlobalFilter并实现GlobalFilter,Ordered两个接口
@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();
}
}));
}
}
2、yml配置文件
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,若一头含有多参则重写一行设置
3、测试
浏览器访问:
- http://localhost:9527/pay/gateway/info
- http://localhost:9527/pay/gateway/get/1
- http://localhost:9527/pay/gateway/filter
请注意,上述示例代码仅供参考,你可能需要根据你的项目需求和实际情况进行适当的调整。