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

如何通过AOP通知Spring WebFlux Web处理器捕获并转换响应式错误

你当前的实现存在两个核心错误:

  1. 调用mono.toFuture().get()是阻塞操作,直接违背了WebFlux的非阻塞设计原则,会导致线程阻塞、性能下降甚至运行异常
  2. 你没有在响应式流内处理异常,直接封装返回的Mono<ResponseEntity>会被Spring WebFlux识别为普通返回值,当做JSON序列化输出,自然不会读取其中的HTTP状态码配置

修复后的AOP实现

你不需要阻塞等待异步结果,直接通过响应式操作符处理流内异常即可:

@Aspect
@Component
class CoreWebAspect {
    @Pointcut("withinApiCorePackage() && @annotation(org.springframework.web.bind.annotation.PostMapping)")
    public void postMappingWebHandler() {
    }

    @Pointcut("within(project.package.prefix.*)")
    public void withinApiCorePackage() {
    }

    @Around("postMappingWebHandler()")
    public Object aroundWebHandler(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        final Object proceed = proceedingJoinPoint.proceed();

        if (proceed instanceof Mono<?> mono) {
            // 用onErrorResume捕获流内异常,全程非阻塞
            return mono.onErrorResume(ConstraintViolationException.class, 
                constraintViolationException -> Mono.just(getResponseEntity(constraintViolationException))
            );
        }

        // 兼容同步抛出异常的场景
        if (proceed instanceof ConstraintViolationException constraintViolationException) {
            return getResponseEntity(constraintViolationException);
        }

        return proceed;
    }

    private ResponseEntity<Set<Violation>> getResponseEntity(final ConstraintViolationException constraintViolationException) {
        final var violations = constraintViolationException.getConstraintViolations().stream().map(violation -> new Violation(
                stream(violation.getPropertyPath().spliterator(), false).map(Node::getName).collect(toList()),
                violation.getMessageTemplate().replaceFirst("^\\{(.*)\\}$", "$1"))
        ).collect(Collectors.toSet());

        return status(UNPROCESSABLE_ENTITY).body(violations);
    }


    @Getter
    @AllArgsConstructor
    private static class Violation {
        private final List<String> path;
        private final String template;
    }
}

补充更优方案

如果没有强制要求用AOP实现,更推荐使用Spring WebFlux原生的全局异常处理器,实现更简洁且适配性更强,不需要处理返回类型适配问题:

@RestControllerAdvice
public class ReactiveGlobalExceptionHandler {
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<Set<Violation>> handleConstraintViolationException(ConstraintViolationException e) {
        final var violations = e.getConstraintViolations().stream().map(violation -> new Violation(
                stream(violation.getPropertyPath().spliterator(), false).map(Node::getName).collect(toList()),
                violation.getMessageTemplate().replaceFirst("^\\{(.*)\\}$", "$1"))
        ).collect(Collectors.toSet());
        return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(violations);
    }

    @Getter
    @AllArgsConstructor
    private static class Violation {
        private final List<String> path;
        private final String template;
    }
}

WebFlux的@RestControllerAdvice原生支持捕获响应式流中发出的异常,不需要额外做异步适配,也不会出现返回状态码异常的问题。


内容的提问来源于stack exchange,提问作者Salathiel Genese

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 18:54:05