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

Spring WebFlux函数式端点多异常处理方案咨询

Spring WebFlux函数式端点多异常处理实现方案

针对你提出的同一端点需根据不同异常返回不同响应的需求,提供两种对应注解控制器@ExceptionHandler效果的实现方式:


方式一:局部异常处理(推荐)

在业务处理的Mono流中使用onErrorResume方法捕获特定异常,直接在端点handler内完成自定义响应,符合函数式编程链式处理风格。

完整代码示例

1. 业务Handler实现

@Service
public class UserHandler {
    private final UserService userService;

    public UserHandler(UserService userService) {
        this.userService = userService;
    }

    public Mono<ServerResponse> getUserById(ServerRequest request) {
        String userId = request.pathVariable("id");
        
        return userService.findById(userId)
                // 正常业务逻辑:返回用户信息
                .flatMap(user -> ServerResponse.ok().bodyValue(user))
                // 捕获UserNotFoundException,返回404响应
                .onErrorResume(UserNotFoundException.class, ex ->
                        ServerResponse.status(HttpStatus.NOT_FOUND)
                                .contentType(MediaType.APPLICATION_JSON)
                                .bodyValue(Map.of(
                                        "errorCode", "USER_NOT_FOUND",
                                        "message", "用户ID:" + userId + "不存在",
                                        "timestamp", LocalDateTime.now().toString()
                                ))
                )
                // 捕获UserException,返回500响应
                .onErrorResume(UserException.class, ex ->
                        ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
                                .contentType(MediaType.APPLICATION_JSON)
                                .bodyValue(Map.of(
                                        "errorCode", "USER_SERVICE_ERROR",
                                        "message", "获取用户失败:" + ex.getMessage(),
                                        "timestamp", LocalDateTime.now().toString()
                                ))
                );
    }
}

2. 路由配置

@Configuration
public class UserRouter {
    @Bean
    public RouterFunction<ServerResponse> userRoutes(UserHandler userHandler) {
        return RouterFunctions.route(
                RequestPredicates.GET("/router/users/{id}")
                        .and(RequestPredicates.accept(MediaType.APPLICATION_JSON)),
                userHandler::getUserById
        );
    }
}

注意事项

  • 异常捕获顺序:若异常存在继承关系(如UserNotFoundException继承自UserException),必须将子类异常的onErrorResume放在父类前面,否则父类会优先捕获所有子类异常。
  • 该方式仅作用于当前端点,不影响其他路由的异常处理逻辑。

方式二:全局异常处理

若需多个端点共享异常处理逻辑,可自定义ErrorWebExceptionHandler,结合请求路径和异常类型实现全局差异化处理。

完整代码示例

@Component
public class CustomErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes,
                                         WebProperties webProperties,
                                         ApplicationContext applicationContext,
                                         ServerCodecConfigurer serverCodecConfigurer) {
        super(errorAttributes, webProperties.getResources(), applicationContext);
        super.setMessageWriters(serverCodecConfigurer.getWriters());
        super.setMessageReaders(serverCodecConfigurer.getReaders());
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(
                // 针对指定端点的异常处理
                RequestPredicates.path("/router/users/{id}"),
                request -> {
                    Throwable error = getError(request);
                    String userId = request.pathVariable("id");
                    
                    if (error instanceof UserNotFoundException) {
                        return ServerResponse.status(HttpStatus.NOT_FOUND)
                                .contentType(MediaType.APPLICATION_JSON)
                                .bodyValue(Map.of(
                                        "errorCode", "USER_NOT_FOUND",
                                        "message", "用户ID:" + userId + "不存在",
                                        "timestamp", LocalDateTime.now().toString()
                                ));
                    } else if (error instanceof UserException) {
                        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
                                .contentType(MediaType.APPLICATION_JSON)
                                .bodyValue(Map.of(
                                        "errorCode", "USER_SERVICE_ERROR",
                                        "message", "获取用户失败:" + error.getMessage(),
                                        "timestamp", LocalDateTime.now().toString()
                                ));
                    }
                    // 其他异常走默认处理
                    return defaultErrorResponse(request, errorAttributes);
                }
        ).andRoute(
                // 其他所有端点的默认异常处理
                RequestPredicates.all(),
                request -> defaultErrorResponse(request, errorAttributes)
        );
    }

    // 全局默认错误响应
    private Mono<ServerResponse> defaultErrorResponse(ServerRequest request, ErrorAttributes errorAttributes) {
        Map<String, Object> errorAttrs = getErrorAttributes(request, ErrorAttributeOptions.defaults());
        return ServerResponse.status((HttpStatus) errorAttrs.get("status"))
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(errorAttrs);
    }
}

适用场景

  • 多个端点需相同异常处理逻辑,避免重复代码。
  • 需要统一管理所有异常的响应格式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 15:35:37