You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot WebFlux WebFilter添加响应耗时Header报错问题

问题根因

报错和功能不符合预期来自两个核心问题:

  • 响应头写入时机错误:doOnSuccess、doOnError、doFinally回调触发时,响应大概率已经完成提交,此时响应头对象会变为只读的ReadOnlyHttpHeaders,调用add方法就会抛出你看到的UnsupportedOperationException。
  • 计时变量存在线程安全问题:requestTime被定义为Filter类的成员变量,而WebFilter在Spring容器中默认是单例,并发请求会反复修改这个共享变量,最终算出的耗时会出现错乱。
修复方案

做两处调整即可:

  1. 将请求起始时间定义为filter方法的局部变量,每个请求进入filter时都会生成独立的计时变量,天然不存在并发覆盖问题
  2. 不要在响应提交后的回调中直接写入响应头,通过ServerHttpResponse提供的beforeCommit钩子注册头写入逻辑,该钩子会在响应头正式发送给客户端前执行,此时响应头仍处于可写状态。

基础实现代码如下:

@Component
public class TMPFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        long requestStartTime = System.nanoTime();
        ServerHttpResponse response = exchange.getResponse();

        // 注册响应提交前回调
        response.beforeCommit(() -> {
            long costNano = System.nanoTime() - requestStartTime;
            response.getHeaders().add("server-timing", Long.toString(costNano));
            return Mono.empty();
        });

        return chain.filter(exchange);
    }
}

如果需要区分请求成功/异常场景返回不同的头字段,可以先在doOnSuccess、doOnError回调中把计算好的耗时存入ServerWebExchange的自定义属性中,最后在beforeCommit回调里统一读取属性写入响应头即可,示例:

@Component
public class TMPFilter implements WebFilter {
    private static final String TIMING_ATTR = "serverTiming";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        long requestStartTime = System.nanoTime();
        ServerHttpResponse response = exchange.getResponse();

        response.beforeCommit(() -> {
            long costNano = exchange.getAttributeOrDefault(TIMING_ATTR, System.nanoTime() - requestStartTime);
            response.getHeaders().add("server-timing", Long.toString(costNano));
            return Mono.empty();
        });

        return chain.filter(exchange)
                .doOnSuccess(v -> exchange.getAttributes().put(TIMING_ATTR, System.nanoTime() - requestStartTime))
                .doOnError(e -> exchange.getAttributes().put(TIMING_ATTR, System.nanoTime() - requestStartTime));
    }
}

内容的提问来源于stack exchange,提问作者Junias Ngoyi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 10:54:22