Spring WebFlux中如何按异常类型处理Mono.zip()异常
对Mono.zipDelayError的认知偏差
Mono.zipDelayError并不会在单个请求失败时返回空Mono,它和普通Mono.zip的核心区别只有错误抛出的时机:
- 普通
zip会在任意一个源Mono抛出错误时立刻终止整个流,取消其他还在执行的源请求,直接抛出第一个遇到的错误 zipDelayError会等待所有源Mono执行完成(不管成功失败),如果有任意源失败,最终依然会抛出聚合后的异常,只是不会提前中断其他源的执行
你之前遇到的「只要getImage失败整个方法就返回错误」是zipDelayError的正常表现,它本身不提供错误容忍、失败降级的能力。
差异化异常处理实现方案
核心思路是在getImage的调用链上单独做异常分流,不要让非阻断类的NOT FOUND错误传递到zip聚合层,改造后的代码如下:
public Mono<ProductResponse> getProductInfo(String domain, Long id) { // 单独处理图片请求的异常逻辑 Mono<ImageResource> imageRequest = proxy.getImage(domain, id) .onErrorResume(e -> { // 根据实际异常类型判断是否为404 NOT FOUND if (isNotFoundError(e)) { // 404场景返回null作为降级值,不抛出错误 return Mono.justOrEmpty(null); } // 判断是否为500 INTERNAL SERVER ERROR if (isInternalServerError(e)) { // 500场景直接抛出自定义异常 return Mono.error(new CustomException(HttpStatus.INTERNAL_SERVER_ERROR)); } // 其余异常按业务需求处理,此处默认透传 return Mono.error(e); }); return Mono.zipDelayError( proxy.getDescription(domain, id), imageRequest ) .map(responses -> { // 适配image为null的场景,避免NPE DescriptionResource desc = responses.getT1(); ImageResource img = responses.getT2(); return img == null ? ProductResponse.builder().description(desc).image(null).build() : ProductResponse.from(desc, img); }); }
关键说明
- 异常判断逻辑需要结合你项目的实际异常类型实现:如果是WebClient调用抛出的
WebClientResponseException,可以直接通过getStatusCode()方法判断响应状态是404还是500;如果是自定义封装的业务异常,根据异常携带的错误码判断即可。 - 保留
zipDelayError的原因是它能完整收集两个请求的异常信息,不会因为某一个请求失败就提前取消另一个请求,排查问题时能拿到更完整的错误上下文。 - 如果你的
ProductResponse.from方法本身已经支持image参数传null,map阶段的逻辑可以简化,直接调用原有方法即可。
内容的提问来源于stack exchange,提问作者Jéssica Carneiro
相关产品推荐
相关产品推荐

