You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Reactive Web异常被包装为500:RouterFunction中WebClient错误处理问题

解决Spring WebFlux中WebClient调用404却返回500的问题

我明白你遇到的困扰——明明在Service里捕获了404并抛出了异常,但客户端收到的却是500错误。这背后的核心原因是Spring WebFlux的默认错误处理器不会自动将HttpClientErrorException的状态码透传给客户端,而是把它归类为服务器内部错误处理。下面给你两种可行的解决方案:

方案一:使用ResponseStatusException(最简单直接)

Spring专门提供了ResponseStatusException,默认错误处理器会识别这个异常携带的状态码,并直接返回对应的HTTP响应。只需要修改Service层的代码即可:

public Mono<String> badEndpoint() {
    return webClient
            .get()
            .uri(configProperties.getNotfound())
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError, clientResponse -> {
                if(clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)){
                    // 替换成ResponseStatusException,让默认处理器识别状态码
                    return Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity not found."));
                } else {
                    return Mono.error(new ResponseStatusException(HttpStatus.BAD_REQUEST, "Client-side error occurred."));
                }
            })
            .bodyToMono(String.class);
}

这样修改后,不需要改动Handler层的代码,默认错误处理器就会把404状态码和对应消息正确返回给客户端,而不是500。

方案二:自定义异常+Handler层错误处理(更灵活可控)

如果你想完全掌控错误响应格式,或者需要针对不同异常做差异化处理,可以用这种方式:

1. 创建自定义异常类

public class CustomWebException extends RuntimeException {
    private final HttpStatus status;

    public CustomWebException(HttpStatus status, String message) {
        super(message);
        this.status = status;
    }

    public HttpStatus getStatus() {
        return status;
    }
}

2. 修改Service层抛出自定义异常

public Mono<String> badEndpoint() {
    return webClient
            .get()
            .uri(configProperties.getNotfound())
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError, clientResponse -> {
                if(clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)){
                    return Mono.error(new CustomWebException(HttpStatus.NOT_FOUND, "Entity not found."));
                } else {
                    return Mono.error(new CustomWebException(HttpStatus.BAD_REQUEST, "Client error."));
                }
            })
            .bodyToMono(String.class);
}

3. 在Handler层捕获异常并转换响应

修改EndpointRouter里的badEndpoint方法,在Mono链上添加onErrorResume处理自定义异常,将其转换为合法的ServerResponse:

public Mono<ServerResponse> badEndpoint(ServerRequest r) {
    return service.badEndpoint()
            // 正常返回时的逻辑
            .flatMap(responseData -> ServerResponse.ok().bodyValue(responseData))
            // 捕获自定义异常,返回对应状态码的响应
            .onErrorResume(CustomWebException.class, ex -> 
                ServerResponse.status(ex.getStatus())
                        .bodyValue(ex.getMessage())
            );
}

为什么之前的onErrorResume没生效?

你之前尝试在Service的Mono上用onErrorResume没成功,大概率是因为错误处理的位置不对:如果只是在Service层处理错误,但最终返回给Handler的Mono仍带着错误状态,而Handler层没有继续处理,这个错误就会冒泡到全局错误处理器,最终返回500。必须在Handler层的Mono链上添加错误处理,把异常转换成合法的ServerResponse,才能避免全局处理器返回500。

内容的提问来源于stack exchange,提问作者StevenPG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 23:47:36