Spring WebClient基于响应体非阻塞抛出自定义错误实现
Spring WebClient 非阻塞错误响应适配实现
核心实现逻辑
整个错误处理全程需要保持非阻塞特性,不能在onStatus回调中同步拉取响应体内容,必须依托WebClient自带的异步反序列化能力,完成响应体结构判断后再抛出对应异常,具体实现点:
- 错误响应存在两种不同的结构,无法用单个POJO直接强转,因此先将错误响应体反序列化为通用的
JsonNode结构,判断存在的字段后匹配对应错误类型 - 所有解析、异常抛出的链路全部基于响应式流的异步回调完成,不调用任何
block类阻塞方法 - 两种结构匹配后,将提取到的错误文本、错误码封装为自定义业务异常抛出,由下游的全局异常处理器统一捕获处理
补全后的可运行代码
// 自定义业务异常,可根据自身项目规范调整字段 public static class BusinessApiException extends RuntimeException { private final int httpStatus; public BusinessApiException(int httpStatus, String message) { super(message); this.httpStatus = httpStatus; } // 可按需补全getter方法 } public Mono<Response> webclient1(...) { return webClient.post().uri(createUserUri) .header(CONTENT_TYPE, APPLICATION_JSON) .body(Mono.just(request), Request.class) .retrieve() .onStatus(HttpStatus::isError, clientResponse -> { // 异步解析响应体为通用Json结构,全程非阻塞 return clientResponse.bodyToMono(JsonNode.class) .flatMap(errorNode -> { // 匹配含error文本字段的错误结构 if (errorNode.has("error")) { String errorMsg = errorNode.get("error").asText().trim(); return Mono.error(new BusinessApiException( clientResponse.statusCode().value(), errorMsg )); } // 匹配含error_code错误码的错误结构 if (errorNode.has("error_code")) { String errorCode = errorNode.get("error_code").asText(); return Mono.error(new BusinessApiException( clientResponse.statusCode().value(), errorCode )); } // 兜底逻辑:未匹配到指定错误结构时抛出通用错误 return Mono.error(new BusinessApiException( clientResponse.statusCode().value(), "接口调用失败,HTTP状态码:" + clientResponse.statusCode() )); }); }) .bodyToMono(Response.class); }
实现注意事项
- 全程不使用
block()方法拉取响应体:整个反序列化过程由WebClient内置的编解码线程异步完成,解析完成后直接将错误信号汇入响应式流,不会阻塞业务调用线程 - 不需要手动缓存响应体:WebClient的
bodyToMono会自动完成小体积错误响应的流读取,不存在响应流被提前消费的问题 - 字段判断必须在异步解析的回调内完成:不能在
onStatus的同步代码块里直接判断字段是否存在,因为此时响应体内容还未被异步拉取到内存中,强行判断会拿到空值 - 自定义异常可根据项目的异常体系调整,只要能携带对应错误信息即可,无需依赖框架内置的WebClient异常类
内容的提问来源于stack exchange,提问作者salman irfan khandu
相关产品推荐
相关产品推荐

