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

Spring WebFlux如何在Controller方法调用后添加自定义后置逻辑

问题原因分析

首先你选择WebFilter作为实现方式是可行的,但现有写法存在两个核心错误导致不生效:

  1. 执行时机完全错误:chain.filter(exchange)返回的Mono<Void>代表的是整个请求处理全流程的异步结果,包含Controller调用、响应编码、响应发送到客户端的所有步骤。当这个Mono触发doOnNext/map/doFinally回调时,响应已经完全提交给客户端了,此时修改状态、加延迟都不会对已经发出去的响应产生任何影响。
  2. 阻塞操作违反响应式规范:你现有的Mode实现用了Thread.sleep,会直接阻塞WebFlux底层的Netty IO线程,导致服务吞吐量暴跌,必须改为非阻塞的响应式实现。

实现方案

首先把Mode接口改造为响应式风格,非阻塞、可拼接进原有响应式流程:

public interface Mode {
    <T> Mono<T> apply(Mono<T> originalResult);
}

各个Mode实现改造如下:

失败模式

import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
import reactor.core.publisher.Mono;

public class FailMode implements Mode {
    @Override
    public <T> Mono<T> apply(Mono<T> originalResult) {
        // 原始业务逻辑执行成功后,返回403错误
        return originalResult.flatMap(res -> Mono.error(new ResponseStatusException(HttpStatus.FORBIDDEN, "Fail mode enabled")));
    }
}

延迟返回模式(长响应模式)

import reactor.core.publisher.Mono;
import java.time.Duration;

public class WaitMode implements Mode {
    private final Duration waitDuration;

    public WaitMode(long waitMs) {
        this.waitDuration = Duration.ofMillis(waitMs);
    }

    @Override
    public <T> Mono<T> apply(Mono<T> originalResult) {
        // 原始逻辑执行成功后,延迟指定时间再返回结果
        return originalResult.delayElement(waitDuration);
    }
}

超时模式

import reactor.core.publisher.Mono;
import java.time.Duration;

public class TimeoutMode implements Mode {
    private final Duration timeout;

    public TimeoutMode(long timeoutMs) {
        this.timeout = Duration.ofMillis(timeoutMs);
    }

    @Override
    public <T> Mono<T> apply(Mono<T> originalResult) {
        // 原始逻辑执行成功后,等待直到超时,也可以根据需求调整为超时直接返回错误
        return originalResult.delayElement(timeout);
    }
}

逻辑织入方案(二选一即可)

方案1:AOP切Controller方法(最简洁,推荐)

通过切面拦截所有Controller方法的返回值,把Mode逻辑拼接进原有的响应式流程:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

@Aspect
@Component
public class ModeAspect {
    // 可通过配置动态切换当前激活的Mode
    private final Mode activeMode = new FailMode();

    @Around("execution(* 你的Controller包路径.*.*(..))")
    public Object wrapControllerResult(ProceedingJoinPoint pjp) throws Throwable {
        Object result = pjp.proceed();
        if (result instanceof Mono<?> monoResult) {
            return activeMode.apply(monoResult);
        }
        // 如果有Flux类型返回值也可自行扩展处理
        return result;
    }
}

方案2:WebFilter实现(适合全局拦截所有请求,包括静态资源等)

如果需要用WebFilter实现,需要调整逻辑执行时机,在响应提交前对返回结果做处理,需要自定义ServerHttpResponse包装类拦截writeWith方法,实现复杂度远高于AOP方案,非必要场景优先选择AOP。


内容的提问来源于stack exchange,提问作者Volodya Lombrozo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 18:06:05