如何在@Before切面中捕获异常并向客户端返回正确的HttpResponse
问题根源
你现在的实现存在两个核心错误,直接导致异常无法流入请求响应链路:
@Before切面无法适配WebFlux异步场景,你主动调用subscribe()后,权限校验的异步流和原接口的响应流完全独立,校验过程抛出的异常不会被请求链路的异常处理器捕获,甚至主线程不会等待校验逻辑执行完成就提前结束,最终无响应返回抛出NoHttpResponseException@Before切面没有能力暂停原方法执行等待异步校验结果,校验还没出结果原接口就已经开始执行,权限校验完全失效
改造方案
1. 替换为@Around切面,串连校验流与原接口流
仅针对返回值为Mono的WebFlux接口做切点,把权限校验逻辑和原方法执行逻辑串到同一条响应流中:
@Around("@annotation(demo.webflux.test.PermittedByRole) && execution(public reactor.core.publisher.Mono *(..))") public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); PermittedByRole annotation = signature.getMethod().getAnnotation(PermittedByRole.class); String operation = annotation.operation(); return testService.testAction(operation) .filter(Boolean::booleanValue) // 校验不通过抛自定义异常,方便统一处理 .switchIfEmpty(Mono.defer(() -> Mono.error(new PermissionDeniedException("权限不足")))) // 校验通过才执行原接口逻辑 .flatMap(allow -> (Mono<?>) joinPoint.proceed()); }
2. 新增自定义异常与全局异常处理器
统一处理权限校验异常,返回标准HTTP 403响应:
// 自定义权限异常 public class PermissionDeniedException extends RuntimeException { public PermissionDeniedException(String message) { super(message); } } // WebFlux全局异常处理器 @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(PermissionDeniedException.class) public Mono<ResponseEntity<Map<String, Object>>> handlePermissionDenied(PermissionDeniedException e) { Map<String, Object> resp = new HashMap<>(); resp.put("code", 403); resp.put("message", e.getMessage()); return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).body(resp)); } }
3. 修正接口阻塞逻辑
WebFlux接口中禁止使用Thread.sleep阻塞事件循环线程,替换为非阻塞延迟方法:
@PermittedByRole(operation = "test") @GetMapping(value = "/oac") public Mono<TestObject> base() { return Mono.delay(Duration.ofSeconds(3)) .thenReturn(new TestObject().setFieldOne("one").setFieldTwo("two")); }
改造后权限校验的异常会在请求响应链路中正常传播,全局异常处理器可以直接捕获返回标准响应,无需额外自定义WebFilter处理。
内容的提问来源于stack exchange,提问作者Aleksey Vasin
相关产品推荐
相关产品推荐

