如何通过AOP通知Spring WebFlux Web处理器捕获并转换响应式错误
你当前的实现存在两个核心错误:
- 调用
mono.toFuture().get()是阻塞操作,直接违背了WebFlux的非阻塞设计原则,会导致线程阻塞、性能下降甚至运行异常 - 你没有在响应式流内处理异常,直接封装返回的
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
相关产品推荐
相关产品推荐

