Spring 5响应式HTTP响应异常处理及微服务场景技术咨询
处理WebFlux远程调用HTTP状态及全局异常捕获方案
我刚好在Spring WebFlux项目里踩过类似的坑,给你一步步拆解完整的解决方案:
1. 自定义异常类
首先得搞一个专属异常来封装远程服务返回的异常HTTP状态,方便后续全局捕获处理:
public class RemoteServiceException extends RuntimeException { private final HttpStatus statusCode; public RemoteServiceException(HttpStatus statusCode, String message) { super(message); this.statusCode = statusCode; } public HttpStatus getStatusCode() { return statusCode; } }
2. 配置WebClient拦截HTTP响应
WebFlux里调用远程REST服务用WebClient,我们可以给它加个过滤器,统一拦截所有响应并检查状态码:
@Configuration public class WebClientConfig { @Bean public WebClient webClient() { return WebClient.builder() .filter(ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { // 只要状态码是错误范畴(4xx、5xx),就抛出我们的自定义异常 if (clientResponse.statusCode().isError()) { return clientResponse.bodyToMono(String.class) .flatMap(errorBody -> Mono.error( new RemoteServiceException( clientResponse.statusCode(), "远程服务调用失败:状态码" + clientResponse.statusCode() + ",错误详情:" + errorBody ) )); } return Mono.just(clientResponse); })) .build(); } }
这里的逻辑很直白:拿到响应先看状态,要是异常就读取响应体的错误信息,封装成自定义异常抛出去。
3. 全局异常处理器(对标Spring 4的@ControllerAdvice)
WebFlux里对应的全局异常处理用@RestControllerAdvice,搭配@ExceptionHandler就能实现和Spring MVC完全一致的全局捕获效果:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(RemoteServiceException.class) public ResponseEntity<ErrorResponse> handleRemoteServiceException(RemoteServiceException ex) { ErrorResponse errorResponse = new ErrorResponse( ex.getStatusCode().value(), ex.getMessage(), LocalDateTime.now() ); return new ResponseEntity<>(errorResponse, ex.getStatusCode()); } // 可以额外加其他异常的兜底处理,比如全局运行时异常 @ExceptionHandler(RuntimeException.class) public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException ex) { ErrorResponse errorResponse = new ErrorResponse( HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器内部错误:" + ex.getMessage(), LocalDateTime.now() ); return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } // 封装错误响应的DTO,按需补充getter/setter public static class ErrorResponse { private int status; private String message; private LocalDateTime timestamp; public ErrorResponse(int status, String message, LocalDateTime timestamp) { this.status = status; this.message = message; this.timestamp = timestamp; } // getter方法 public int getStatus() { return status; } public String getMessage() { return message; } public LocalDateTime getTimestamp() { return timestamp; } } }
这个处理器会自动捕获我们抛出的RemoteServiceException,然后返回结构化的错误响应,和你熟悉的Spring 4中@ControllerAdvice的效果一模一样。
4. 实际调用远程服务
现在你在业务代码里用配置好的WebClient调用远程服务时,只要对方返回异常状态码,就会自动触发异常抛出,然后被全局处理器捕获并返回统一响应:
@Service public class RemoteCallService { private final WebClient webClient; public RemoteCallService(WebClient webClient) { this.webClient = webClient; } public Mono<User> getUserById(String userId) { return webClient.get() .uri("https://remote-service/api/users/{id}", userId) .retrieve() .bodyToMono(User.class); } }
比如远程服务返回404时,WebClient的过滤器会立刻抛出RemoteServiceException,随后全局处理器会返回包含404状态码和错误信息的响应体。
这样一套流程走下来,就完美实现了你要的:统一处理远程调用的所有HTTP状态,异常状态抛异常,再通过全局处理器统一返回响应的需求。
内容的提问来源于stack exchange,提问作者Dina Bogdan
相关产品推荐
相关产品推荐

