如何使用@ControllerAdvice处理Spring WebFlux中WebClient请求错误
核心原理
Spring WebFlux 中 WebClient 返回的 Mono/Flux 是冷流:只有当流被订阅时,请求才会真正发送,异常才会在执行链中传播。当前代码存在两个核心问题导致异常无法被全局拦截:
- 对外提供的
send接口方法中没有执行WebClient调用逻辑,直接返回200状态,WebClient的流从未被触发订阅,自然不会产生异常 - 即使调用了WebClient,如果手动调用
subscribe()触发流而非将流返回给Spring WebFlux核心容器管理,异常会落到自定义订阅的错误钩子,不会进入全局异常处理链
适配步骤
1. 修正自定义异常定义
确保自定义异常继承RuntimeException,支持接收错误信息、原始状态码参数:
public class WrongCredentialsException extends RuntimeException { private final int rawStatusCode; public WrongCredentialsException(String message, int rawStatusCode) { super(message); this.rawStatusCode = rawStatusCode; } public int getRawStatusCode() { return rawStatusCode; } }
2. 修正WebClient调用逻辑
onStatus判断逻辑中,拿到错误响应后把状态码和错误体一起传入自定义异常抛出,避免响应体重复消费:
public Mono<TResponse> callRemoteService(Dto dto) { return webClient .post() .uri("/your/remote/api") // 替换为实际远程接口地址 .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body( BodyInserters .fromFormData("param1", dto.getParam1()) // 替换为实际表单参数 .with("param2", dto.getParam2()) ) .retrieve() .onStatus(HttpStatus::isError, response -> response.bodyToMono(String.class) .flatMap(errorBody -> Mono.error(new WrongCredentialsException(errorBody, response.statusCode().value()))) ) .bodyToMono(TResponse.class); }
3. 修正Controller接口逻辑
必须将WebClient返回的响应流直接返回给Spring管理,由框架负责订阅触发请求,异常会自动沿执行链抛到全局处理器:
@PostMapping("/send") public Mono<ResponseEntity<String>> send(@RequestBody Dto dto) { log.debug("An notification has been send to user"); return callRemoteService(dto) .map(response -> ResponseEntity.ok("success")); }
注意:绝对不要在Controller方法内部手动调用
.subscribe()触发WebClient请求,否则异常脱离WebFlux主执行链,全局处理器无法捕获。
4. 修正全局异常处理器
补全错误响应字段,适配响应式执行链:
@ControllerAdvice @Slf4j public class ApplicationErrorHandler { @ExceptionHandler(WrongCredentialsException.class) public ResponseEntity<ErrorResponse> handleResponseException(WrongCredentialsException ex) { log.error("Error from WebClient - Status {}, Body {}", ex.getRawStatusCode(), ex.getMessage(), ex); ErrorResponse error = new ErrorResponse( "REMOTE_CALL_ERROR", ex.getMessage() ); // 远程返回400时不要统一返回500,可根据实际业务调整状态码 return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(error); } }
验证方式
启动服务后调用对外的/send接口,当远程服务返回400/500等错误状态时,会自动触发WrongCredentialsException抛出,全局处理器会捕获异常并返回结构化的错误信息给客户端,不会出现异常丢失的问题。
内容的提问来源于stack exchange,提问作者skyho
相关产品推荐
相关产品推荐

