Micronaut 响应过滤器返回空 Publisher 后变 503,显式传回当前响应
受影响的 Micronaut 响应过滤器返回空 Publisher 会中断响应接续。原样放行时显式发出当前响应,不能把空流、可空返回值与同步方法约定混用。
一个响应过滤器只想在条件不满足时原样放行,返回 Publishers.empty() 后,正常接口却变成了空体 503。这时应先检查过滤方法的返回类型,而不是把 503 当成下游服务不可用。
在受影响的 Micronaut 注解过滤器路径中,@ResponseFilter 返回 Publisher 时,空流表示没有发出任何元素。后续处理挂在 flatMap 上;没有元素,接续响应处理就不会执行。它与同步方法返回 null 的放行约定不同,不能直接照搬。
保留当前响应的明确写法是发出这个响应对象:
import io.micronaut.core.async.publisher.Publishers;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.annotation.ResponseFilter;
import org.reactivestreams.Publisher;
@ResponseFilter
Publisher<MutableHttpResponse<?>> filterResponse(
HttpRequest<?> request, MutableHttpResponse<?> response) {
if (!applies(request)) {
return Publishers.just(response);
}
// 此处分支也要按业务返回实际响应或实际的异步处理结果。
return processResponse(request, response);
}
applies 和 processResponse 代表应用自己的判断与处理方法。关键是原样放行分支发出传入的 response,不新建一个丢失状态码、header 或 body 的响应。
空 Publisher 和 Java null 还要分开检查。原故障案例中,标为 @Nullable 的空或 null Publisher 都出现空体 503,未标可空的方法返回 null 则出现 NPE。给方法加 @Nullable 并不是空流放行方案。Publishers.just(response) 避开了这两个分支。
Core 5.2.15 的正式源码中,这条 reactive 返回路径仍直接从 Publisher 转成执行流后 flatMap 接续;不能把处理空 Publisher 的补丁记录理解成该正式版本已经自动放行。若你仍在这条路径上,先采用明确发出响应的方案,再按后续正式版本的实际行为决定是否去掉它。
为过滤器加一个“不适用”的端点或请求条件,检查状态码、header 和 body 都与未过滤时一致。再测“适用”分支,确认异步处理只发生一次,响应仍完成;如果其中一支返回空流,应单独观察是否仍会结束交换。这里的判断限于 @ResponseFilter 方法返回 Publisher 的路径,经典 HttpServerFilter 如何调用 chain.proceed() 是另一份契约,不能把本例当作省略链调用的依据。


