这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战。
上篇文章我们介绍了SpringCloud Gateway Filter的相关概念,并解读了一个全局过滤器——RouteToRequestUrlFilter的filter方法,这篇文章我们来介绍一下两个局部过滤器。
解读SpringCloud Gateway Filter(下)
全局过滤器是实现GlobalFilter, Ordered接口,局部过滤器是实现GatewayFilter, Ordered接口,然后再去实现对应的apply方法。
1、添加前缀的局部过滤器 PrefixPathGatewayFilterFacotry
可以看出来,这是一个局部过滤器的工厂,在工厂内实现了一个局部过滤器。有一个属性 PREFIX_KEY,是指明需要在url上增加的前缀。
接下来看下代码实现。
public GatewayFilter apply(PrefixPathGatewayFilterFactory.Config config) {
return new GatewayFilter() {
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
boolean alreadyPrefixed = (Boolean)exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_ALREADY_PREFIXED_ATTR, false);
if (alreadyPrefixed) {
return chain.filter(exchange);
} else {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ALREADY_PREFIXED_ATTR, true);
ServerHttpRequest req = exchange.getRequest();
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, req.getURI());
String newPath = config.prefix + req.getURI().getRawPath();
ServerHttpRequest request = req.mutate().path(newPath).build();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, request.getURI());
if (PrefixPathGatewayFilterFactory.log.isTraceEnabled()) {
PrefixPathGatewayFilterFactory.log.trace("Prefixed URI with: " + config.prefix + " -> " + request.getURI());
}
return chain.filter(exchange.mutate().request(request).build());
}
}
public String toString() {
return GatewayToStringStyler.filterToStringCreator(PrefixPathGatewayFilterFactory.this).append("prefix", config.getPrefix()).toString();
}
};
}
alreadyPrefixed是boolean类型,用来校验一下是否已经添加前缀了,通过exchange.getAttributeOrDefault中的GATEWAY_ALREADY_PREFIXED_ATTR属性来判断,默认值为false。
接下来使用config的Prefix去构造新的路径,新的HttpRequest。先根据exchange.getRequest()获取请求对应的request,然后根据request获取到对应的url,然后在url上去添加一个前缀生成新的path,用这个新的path去构造新的ServerHttpRequest。
举个例子,假如有个简单的静态路由配置如下:
spring:
cloud:
routes:
-id: sheep
uri: http//example/org
filters:
- PrefixPath=/mypath
假如现在有个请求/hello,命中了上方的配置,那么这个添加前缀的局部过滤器就会将这个请求变成:http://example/org/mypath/hello。
2、去除前缀的局部过滤器 StripPrefixGatewayFilterFactory
strip是跳过的意思,所以该过滤器是用来去掉前缀的。
public GatewayFilter apply(StripPrefixGatewayFilterFactory.Config config) {
return new GatewayFilter() {
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, request.getURI());
String path = request.getURI().getRawPath();
String newPath = "/" + (String) Arrays.stream(StringUtils.tokenizeToStringArray(path, "/")).skip((long)config.parts).collect(Collectors.joining("/"));
newPath = newPath + (newPath.length() > 1 && path.endsWith("/") ? "/" : "");
ServerHttpRequest newRequest = request.mutate().path(newPath).build();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
return chain.filter(exchange.mutate().request(newRequest).build());
}
public String toString() {
return GatewayToStringStyler.filterToStringCreator(StripPrefixGatewayFilterFactory.this).append("parts", config.getParts()).toString();
}
};
}
通过request.getURI().getRawPath()去获取原始的URI,然后根据配置的parts部分去执行.skip(config.parts)去掉前缀。parts是int类型,也就是指定一个数字,去掉几个前缀的部分。
然后将获取到的新path去构造一个新的request再进行转发。
举个例子,假如有个简单的静态路由配置如下:
spring:
cloud:
routes:
-id: sheep
uri: http//example/org
predicates:
- Path=/name/**
filters:
- StripPrefix=2
如果这时候有个请求是/name/bar/foo命中了上方的配置,那么这个请求就会变成/foo,要注意的是,这个stripPrefix=2指的是去掉两个"/xxx",而不是字符!
SpringCloud Gateway Filter 的执行流程
1、过滤器有优先级之分,Order越大,优先级就越低,越晚被执行。可以通过getOrder方法获取Order。
2、全局过滤器所有的请求都会执行,这是默认的行为,不需要做额外的操作,针对自定义的全局过滤器也是如此。
3、局部过滤器只有配置的请求才会执行,配置在路由配置中,之后文章会介绍如何配置才会使其生效。
自定义局部过滤器的步骤
1、自定义局部过滤器实现GatewayFilter, Ordered 这两个接口中的相应方法。
2、将自定义的局部过滤器加入到过滤器工厂中,并将工厂加入到Spring IOC容器中,工厂继承 AbstractGatewayFilterFactory 抽象类。
3、配置文件中配置过滤器,使其生效。