Micronaut HttpServerFilter响应处理顺序反转问题咨询
我刚接触反应式编程,目前在Micronaut服务器中创建两个HttpServerFilter用于处理响应,简化代码示例如下:
FirstFilter
@Filter("/**") public class FirstFilter implements HttpServerFilter { @Override public int getOrder(){ return ServerFilterPhase.FIRST.before(); } @Override public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) { System.out.println("Returning publisher in FirstFilter!"); return Publishers.then( chain.proceed(request), mutableHttpResponse -> { System.out.println("Mapping response in first filter!"); mutableHttpResponse.getBody(ServiceHttpResponse.class) .ifPresent(resp -> mutableHttpResponse.status(resp.getStatus())); }); } }
SecondFilter
@Filter("/**") public class SecondFilter implements HttpServerFilter { @Override public int getOrder(){ return ServerFilterPhase.FIRST.after(); } @Override public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) { System.out.println("Returning publisher in SecondFilter!"); return Publishers.then( chain.proceed(request), mutableHttpResponse -> { System.out.println("Mapping response in second filter!"); mutableHttpResponse.getBody(ServiceHttpResponse.class) .ifPresent(resp -> mutableHttpResponse.body(resp.format())); }); } }
请求执行后的日志:
Returning publisher in FirstFilter! Returning publisher in SecondFilter! Mapping response in second filter! Mapping response in first filter!
从日志可见,doFilter方法执行顺序正确,但反应式流中的响应映射逻辑执行顺序反转。我的需求是先通过响应体(ServiceHttpResponse)设置响应状态,再设置格式化后的响应体,但因顺序反转导致状态信息丢失,多次测试多过滤器场景均出现该顺序反转问题,请问该现象的原因是什么?
原因分析
这是由Micronaut过滤器的洋葱执行模型结合反应式流的回调时机导致的:
请求阶段的执行顺序:
过滤器的getOrder()决定了请求传递的优先级——FirstFilter的FIRST.before()优先级更高,请求先进入它的doFilter方法,打印日志后调用chain.proceed(request)将请求传递给SecondFilter,后者执行自身doFilter后再把请求传递到后端处理器。响应阶段的回调顺序:
Publishers.then(publisher, callback)的作用是:当上游publisher(即chain.proceed(request)返回的响应流)完成时,执行传入的回调逻辑。而响应流是从后端处理器反向返回的——先回到最内层的SecondFilter,触发它的then回调;之后才回到外层的FirstFilter,触发它的then回调,这就是日志中响应处理顺序反转的核心原因。状态丢失的直接诱因:
你的需求是先设置响应状态再格式化响应体,但当前回调顺序是先执行SecondFilter的格式化逻辑——此时响应体被替换为格式化后的内容,原ServiceHttpResponse实例已不存在;后续FirstFilter尝试从响应体中获取ServiceHttpResponse时无法拿到,自然无法完成状态设置。
内容的提问来源于stack exchange,提问作者Mateusz

