
在传统的spring mvc应用中,@controlleradvice结合@exceptionhandler能够有效地捕获控制器层抛出的异常并进行统一处理。然而,在spring webflux的响应式编程模型中,异常的传播方式有所不同。当异常在一个响应式流(如mono或flux)的内部(例如在map、flatmap等操作符中)被抛出时,它会作为流的错误信号向下游传播,而不是直接中断当前线程的执行并被传统的@exceptionhandler捕获。
例如,在以下GlobalFilter的filter方法中,当webClient调用外部服务后,在map操作符内部抛出AuthorizationForbiddenException:
// 部分GlobalFilter代码片段
return webClient.get()
.uri("http://uaa", uriBuilder -> uriBuilder
.path("/validate-token")
.queryParam("token", authToken).build()).retrieve()
.bodyToMono(TokenValidationGetResource.class)
.map(tokenValidationGetResource -> {
if (!tokenValidationGetResource.isValid()) {
log.debug("token is not valid");
throw new AuthorizationForbiddenException(AuthorizationForbiddenExceptionTitleEnum.TOKEN_NOT_VALID, "Token is not valid"); // 此处抛出异常
}
// ... 其他逻辑
}).flatMap(chain::filter);此时,即使存在一个配置了@ExceptionHandler({AuthorizationForbiddenException.class})的@ControllerAdvice,也无法捕获到这个异常。这是因为@ControllerAdvice主要处理由DispatcherHandler直接分发到控制器方法时产生的同步异常,或者在WebFlux中,处理由路由函数直接抛出的异常。对于在响应式流内部,通过onError信号传播的异常,需要专门的响应式异常处理机制。
Spring WebFlux提供了AbstractErrorWebExceptionHandler和ErrorAttributes接口来处理响应式流中的异常。
为了解决上述问题,我们需要创建两个组件:一个自定义的GlobalErrorWebExceptionHandler和一个自定义的GlobalErrorAttributes。
GlobalErrorAttributes负责从请求中提取原始异常,并将其转换为我们希望返回给客户端的错误信息结构。
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
@Component
public class GlobalErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
// 获取原始错误信息
Throwable error = getError(request);
// 尝试将错误转换为我们自定义的BaseException类型
AbstractBaseException baseException = null;
try {
if (error instanceof AbstractBaseException) {
baseException = (AbstractBaseException) error;
}
} catch (Exception e) {
// 如果转换失败,保留原始错误
// 这里可以添加日志记录
}
Map<String, Object> errorResources = new HashMap<>();
// 根据自定义异常或通用错误信息填充响应体
// 示例:定义你希望在响应体中返回的属性
errorResources.put("timestamp", Instant.now().toEpochMilli());
errorResources.put("status", baseException != null ? baseException.getStatus().value() : HttpStatus.INTERNAL_SERVER_ERROR.value());
errorResources.put("code", baseException != null ? baseException.getTitle().getCode() : "UNKNOWN_ERROR");
errorResources.put("title", baseException != null ? baseException.getTitle().toString() : "Internal Server Error");
errorResources.put("detail", baseException != null ? baseException.getMessage() : "An unexpected error occurred.");
errorResources.put("developerMessage", error.getClass().getName());
// 如果需要,可以添加更多信息,例如请求路径等
// errorResources.put("path", request.path());
return errorResources;
}
}说明:
GlobalErrorWebExceptionHandler负责定义错误请求的路由规则,并使用GlobalErrorAttributes提供的错误信息来构建最终的HTTP响应。
import org.springframework.boot.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.server.WebProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import java.util.Map;
@Component
@Order(-2) // 确保此处理器在Spring Boot默认错误处理器之前被执行
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
public GlobalErrorWebExceptionHandler(GlobalErrorAttributes globalErrorAttributes,
ApplicationContext applicationContext,
ServerCodecConfigurer serverCodecConfigurer) {
super(globalErrorAttributes, new WebProperties.Resources(), applicationContext);
// 设置消息读写器,以便能够处理JSON等媒体类型
super.setMessageWriters(serverCodecConfigurer.getWriters());
super.setMessageReaders(serverCodecConfigurer.getReaders());
}
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
// 定义路由函数,将所有请求(RequestPredicates.all())路由到renderErrorResponse方法
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
// 从ErrorAttributes中获取错误属性,这些属性由GlobalErrorAttributes提供
final Map<String, Object> errorPropertiesMap = getErrorAttributes(request, ErrorAttributeOptions.defaults());
// 从错误属性中提取状态码,如果无法获取,则默认为500 Internal Server Error
HttpStatus statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
if (errorPropertiesMap.containsKey("status")) {
Object statusObj = errorPropertiesMap.get("status");
if (statusObj instanceof Integer) {
statusCode = HttpStatus.valueOf((Integer) statusObj);
} else if (statusObj instanceof String) {
try {
statusCode = HttpStatus.valueOf(Integer.parseInt((String) statusObj));
} catch (NumberFormatException e) {
// Ignore, use default
}
}
}
// 构建并返回ServerResponse
return ServerResponse.status(statusCode)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorPropertiesMap));
}
}说明:
完成上述两个类的实现后,当GlobalFilter中的WebClient操作符内部抛出AuthorizationForbiddenException时,该异常将作为错误信号在响应式流中传播,最终被GlobalErrorWebExceptionHandler捕获。GlobalErrorWebExceptionHandler会调用GlobalErrorAttributes来生成包含详细错误信息的JSON响应,并返回给客户端。
示例错误响应(基于自定义属性):
{
"timestamp": 1678886400000,
"status": 403,
"code": "AUTH_FORBIDDEN_TOKEN_NOT_VALID",
"title": "TOKEN_NOT_VALID",
"detail": "Token is not valid",
"developerMessage": "com.example.exception.AuthorizationForbiddenException"
}通过实现自定义的AbstractErrorWebExceptionHandler和ErrorAttributes,我们为Spring Boot WebFlux应用构建了一个强大且灵活的全局响应式异常处理机制。这确保了无论异常在响应式流的何处抛出,都能被统一捕获、处理,并以标准化的格式返回给客户端,极大地提升了应用的健壮性和可维护性。理解响应式编程中异常传播的特性,是构建高效WebFlux应用的关键一步。
以上就是Spring Boot WebFlux中响应式流异常的统一处理指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号