如何在 WebClient 异常时返回自定义 ResponseEntity?
发布时间 - 2026-01-09 00:00:00 点击率:次本文介绍在 spring webflux 中使用 webclient 调用远程接口时,当发生如 401 unauthorized 等特定异常,如何优雅地捕获并统一返回符合业务规范的 `responseentity
在响应式编程中,WebClient 默认将 HTTP 错误状态(如 401、403、500)封装为 WebClientResponseException 及其子类(如 WebClientResponseException.Unauthorized),并不会自动触发 Spring MVC 的异常处理机制——因为 WebClient 运行在服务调用方(客户端)逻辑中,而非控制器层。因此,直接在链式调用中使用 .onErrorMap() 无法返回 ResponseEntity,原因在于:
- onErrorMap 仅用于转换异常类型(返回新异常),不能生成 Mono
>; - toEntity(Class
) 返回的是 Mono >,但一旦发生错误,整个 Mono 就会以错误终止,需显式恢复(recover)才能继续流式返回。
✅ 正确做法是:结合 onStatus + onErrorResume 实现声明式错误响应构造,避免依赖 @ControllerAdvice(该方案适用于 被调用方 的 Controller 异常处理,不适用于 WebClient 调用方的响应定制)。
✅ 推荐方案:在 WebClient 链路中内聚处理异常响应
public Mono> getOrder(String orderId, String token) { return webClient.get() .uri(orderUrl + "/" + orderId) .header(HttpHeaders.CACHE_CONTROL, "no-cache") .header(HttpHeaders.ACCEPT, "*/*") .header(HttpHeaders.ACCEPT_ENCODING, " deflate, br") .header(HttpHeaders.CONNECTION, "keep-alive") .header(HttpHeaders.AUTHORIZATION, "Bearer " + token) .retrieve() // 拦截特定状态码(如 401),转为自定义错误响应 .onStatus(HttpStatus::is4xxClientError, response -> { if (response.statusCode().equals(HttpStatus.UNAUTHORIZED)) { MagentoAdobeOrderResponse errorBody = new MagentoAdobeOrderResponse(); // 可在此设置错误字段,如 code="UNAUTHORIZED", message="Token expired" return Mono.just(new WebClientResponseException.Unauthorized( "Unauthorized", null, null, new byte[0], Charset.defaultCharset())); } return Mono.empty(); }) // 将 WebClientResponseException 映射为 ResponseEntity .toEntity(MagentoAdobeOrderResponse.class) .onErrorResume(WebClientResponseException.class, ex -> { HttpStatus status = ex.getStatusCode(); MagentoAdobeOrderResponse fallback = buildErrorResponse(ex); // 自定义错误体构建逻辑 return Mono.just(ResponseEntity.status(status).body(fallback)); }); }
其中 buildErrorResponse() 示例:
private MagentoAdobeOrderResponse buildErrorResponse(WebClientResponseException ex) {
MagentoAdobeOrderResponse resp = new MagentoAdobeOrderResponse();
resp.setCode(String.valueOf(ex.getRawStatusCode()));
resp.setMessage(ex.getStatusText());
resp.setTimestamp(Instant.now());
// 可选:解析原始响应体中的错误详情(若服务端返回 JSON 错误结构)
try {
JsonNode errorNode = new ObjectMapper().readTree(ex.getResponseBodyAsString());
resp.setDetails(errorNode.toString());
} catch (Exception ignored) {}
return resp;
}⚠️ 注意事项
- ❌ 不要滥用 @ControllerAdvice 处理 WebClient 异常:它只对 @RestController 抛出的异常生效,对 WebClient 内部异常无感知;
- ✅ onStatus 是拦截非 2xx 响应的首选方式,比 onErrorMap 更精准(后者仅捕获已抛出的异常);
- ✅ 使用 onErrorResume 替代 onErrorMap 来实现“错误 → 新 Mono”的转换,从而返回 ResponseEntity;
- ? 若需全局复用该逻辑,可封装为 WebClient 的 ExchangeFilterFunction,实现跨服务调用的一致错误响应策略。
✅ 总结
返回自定义 ResponseEntity 的核心在于:在响应式流中主动拦截错误状态,并通过 onErrorResume 恢复为合法的 Mono
# js
# json
# node
# adobe
# app
# ai
# keep-alive
# 状态码
# 响应式编程
# spring mvc
# mvc
# spring
# 封装
# 子类
# 接口
# class
# 对象
# http
# 自定义
# 抛出
# 链式
# 而非
# 的是
# 在此
# 适用于
# 可选
# 来实现
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel Fortify是什么,和Jetstream有什么关系
Laravel如何创建和注册中间件_Laravel中间件编写与应用流程
高端云建站费用究竟需要多少预算?
如何在腾讯云免费申请建站?
如何快速上传自定义模板至建站之星?
品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?
敲碗10年!Mac系列传将迎来「触控与联网」双革新
Claude怎样写结构化提示词_Claude结构化提示词写法【教程】
动图在线制作网站有哪些,滑动动图图集怎么做?
uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址
node.js报错:Cannot find module 'ejs'的解决办法
历史网站制作软件,华为如何找回被删除的网站?
制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?
Laravel怎么上传文件_Laravel图片上传及存储配置
Internet Explorer官网直接进入 IE浏览器在线体验版网址
Laravel全局作用域是什么_Laravel Eloquent Global Scopes应用指南
Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】
详解vue.js组件化开发实践
Python文件流缓冲机制_IO性能解析【教程】
如何在HTML表单中获取用户输入并结合JavaScript动态控制复利计算循环
Laravel如何实现文件上传和存储?(本地与S3配置)
Laravel用户认证怎么做_Laravel Breeze脚手架快速实现登录注册功能
Linux系统命令中screen命令详解
高端建站如何打造兼具美学与转化的品牌官网?
Zeus浏览器网页版官网入口 宙斯浏览器官网在线通道
Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置
Laravel如何理解并使用服务容器(Service Container)_Laravel依赖注入与容器绑定说明
JS弹性运动实现方法分析
三星、SK海力士获美批准:可向中国出口芯片制造设备
Win11怎么关闭透明效果_Windows11辅助功能视觉效果设置
装修招标网站设计制作流程,装修招标流程?
如何在VPS电脑上快速搭建网站?
javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】
Python文件操作最佳实践_稳定性说明【指导】
如何用低价快速搭建高质量网站?
Laravel API资源(Resource)怎么用_格式化Laravel API响应的最佳实践
香港服务器部署网站为何提示未备案?
PHP 实现电台节目表的智能时间匹配与今日/明日轮播逻辑
网站制作壁纸教程视频,电脑壁纸网站?
最好的网站制作公司,网购哪个网站口碑最好,推荐几个?谢谢?
Laravel如何实现邮件验证激活账户_Laravel内置MustVerifyEmail接口配置【步骤】
jQuery中的100个技巧汇总
公司门户网站制作流程,华为官网怎么做?
Android仿QQ列表左滑删除操作
HTML透明颜色代码在Angular里怎么设置_Angular透明颜色使用指南【详解】
弹幕视频网站制作教程下载,弹幕视频网站是什么意思?
百度输入法ai组件怎么删除 百度输入法ai组件移除工具
,交易猫的商品怎么发布到网站上去?
LinuxShell函数封装方法_脚本复用设计思路【教程】
Laravel如何使用集合(Collections)进行数据处理_Laravel Collection常用方法与技巧


deflate, br")
.header(HttpHeaders.CONNECTION, "keep-alive")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.retrieve()
// 拦截特定状态码(如 401),转为自定义错误响应
.onStatus(HttpStatus::is4xxClientError,
response -> {
if (response.statusCode().equals(HttpStatus.UNAUTHORIZED)) {
MagentoAdobeOrderResponse errorBody = new MagentoAdobeOrderResponse();
// 可在此设置错误字段,如 code="UNAUTHORIZED", message="Token expired"
return Mono.just(new WebClientResponseException.Unauthorized(
"Unauthorized", null, null, new byte[0], Charset.defaultCharset()));
}
return Mono.empty();
})
// 将 WebClientResponseException 映射为 ResponseEntity
.toEntity(MagentoAdobeOrderResponse.class)
.onErrorResume(WebClientResponseException.class, ex -> {
HttpStatus status = ex.getStatusCode();
MagentoAdobeOrderResponse fallback = buildErrorResponse(ex); // 自定义错误体构建逻辑
return Mono.just(ResponseEntity.status(status).body(fallback));
});
}