spring webflux过滤器
spring5 webflux模块中的webfilter模型与其他过滤器或拦截器是类似的,众多filter可以组成过滤链,过滤器链的图示就不画了。
webflux filter目前没有servlet filter中的url pattern功能,它对所有请求进行过滤,如果要实现url pattern可以自己在过滤器中匹配url。
过滤器的调用顺序
如果有多个webflux webfliter,那其调用的先后顺序如何确定?在过滤链中的调用顺序遵循如下规则:
1.webfilter实现类上有@Order注解的排在没有该注解的前面.
2. 对有@Order注解的webfliter,order值(为有符号的int)小的排在前面。
3. 对没有@Order注解的,按实现类的全限定类名(含有包名)的字典序升序排列。
4. 过滤器也是spring bean,绝大多数情况下是单例,如果不是单例,这些实例之间
调用顺序估计按照实例化的时间先后排列。
webfliter实现
webflux中的filter要实现org.springframework.web.server.WebFilter接口,该接口只有一个方法filter。
两个过滤器实现:RequestFilter1和RequestFilter2.
@Component
@Order(-5)
public class RequestFilter1 implements WebFilter {
@Override
//返回Mono<Void> 表示该过滤器的处理已经结束。
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
/*ServerWebExchange寓意交换/兑换(exchange):用请求换回响应. 所以ServerWebExchange包含有成对的http请求对象ServerHttpRequest和http响应对象ServerHttpResponse.
ServerHttpRequest是webflux controller中的http请求对象,类似webmvc中的HttpServletRequest
*/
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//获取请求http头appKey值
String appKey = request.getHeaders().getFirst("appKey");
System.out.println("appKey="+appKey);
//添加请求属性key和value
exchange.getAttributes().put("k1", "v1");
/*过滤器链的概念都是类似的,调用过滤器链的filter方法将请求转到下一个filter,如果该filter是最后一 个filter,那就转到
该请求对应的handler,也就是controller方法或函数式endpoint */
return chain.filter(exchange);
}
}
}
@Component
//@Order(1)
public class RequestFilter2 implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 获取上个filter设置的属性k1
String value = (String)exchange.getAttributes().get("k1");
System.out.println("value="+value);
return chain.filter(exchange);
}
}
}
过滤器的通常用法
过滤器通常用来实现一些横切的逻辑,例如认证授权、日志记录。
下面的过滤器检查http请求头中是否有token头,如果没有则将请求forward转到/auth/error,由对应的controller进行处理。这个也演示了如何用WebFilter来forward请求。
@Component
public class AuthFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
//ServerHttpResponse response = exchange.getResponse();
String tokenValue = request.getHeaders().getFirst("token");
if(null == token){
/*http头中没有appKey,修改请求的目标url为/auth/error
request.mutate返回一个请求构建器(builder design pattern),path方法修改请求的url,build方法返回新的request
最后调用chain.filter将请求forward到修改后的url:/auth/error
*/
ServerHttpRequest authErrorReq = request.mutate().path("/auth/error").build();
//erverWebExchange.mutate类似,构建一个新的ServerWebExchange
ServerWebExchange authErrorExchange = exchange.mutate().request(authErrorReq).build();
return chain.filter(autherrorExchange);
}
else{
return chain.filter(exchange);
}
}
}
}
也可在过滤器中直接处理请求,跳过后面的filter和controller
@Component
public class AuthFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
ServerHttpRequest request = serverWebExchange.getRequest();
ServerHttpResponse response = serverWebExchange.getResponse();
String tokenValue = request.getHeaders().getFirst("token");
if(null == token){
// 直接返回响应结果,跳过后面的filter和controller
response.setStatusCode(HttpStatus.OK);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.writeWith(Mono.just(response.bufferFactory().wrap("{\"msg\":\"no token\"}".getBytes())));
}
else {
return webFilterChain.filter(serverWebExchange);
}
}
}
}
过滤器后处理
前面的演示都还是前处理,在后面的filter完成之后,控制流回到本filter。
可以在WebFilterChain的filter方法后面加入后处理逻辑,这个要熟悉spring reactor3的运算符/算子(operator), 例如doFinally、then、thenEmpty、thenMany、map、flatMap等,后处理逻辑分布在这些运算符中,只要最终返回Mono<Void>即可。
@Component
public class PostProcessingFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
//后处理,打印完成信号的值
return chain.filter(exchange).doFinally(s -> System.out.println("signal="+s.toString()));
/* 其他几种
* or 1: 建议尽量采用链式的fluent连贯写法
* Mono<Void> completeMono = chain.filter(exchange);
* return completeMono.doFinally(s -> System.out.println("signal="+s.toString()));
*/
//or 2: return chain.filter(exchange).thenEmpty(other);
//or 3: return chain.filter(exchange).thenMany(other).map(..)....then();
}
}