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

为什么我的Controller Advice与Mono配合无法正常工作?

问题描述

我编写了如下Controller:

@PostMapping(consumes = "application/json", value = "id")
public Mono<MyResponse> filtering(@Valid @RequestBody MyRequest request) {
    return Mono.fromCallable(() -> peopleFiltering.filter(
                    request.getEntity()))
            .flatMap(result -> result.map(MyResponseMapper::mapToResponse));
}

以及对应的Controller Advice:

@ControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler({NotFoundException.class})
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ResponseBody
    public Mono<BankFilteringError> handleNotFound(NotFoundException ex) {
        MyError error = new MyError();
        error.setCode("404");
        error.setDescription("404 NOT_FOUND");
        error.setInstance("instance");
        error.setMessage("Not Found");

        return Mono.just(error);
    }

    @ExceptionHandler({BadRequestException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public BankFilteringError handleBadRequest(BadRequestException ex) {
        MyError error = new MyError();
        error.setCode("400");
        error.setDescription("Invalid Request Body");
        error.setInstance("instance");
        error.setMessage("Bad Request"); // Include the specific error message
        return error;
    }
}

自定义异常类如下:

public class NotFoundException extends RuntimeException {
    public NotFoundException(String errorMessage) {
        super(errorMessage);
    }
}

和

public class BadRequestException extends RuntimeException {
    public BadRequestException(String message) {
        super(message);
    }
}

但使用Postman调用接口时,返回404或400状态码却没有自定义响应体。我尝试添加:

.switchIfEmpty(Mono.error(request.getEntity() == null ?
                        new BadRequestException("Invalid Request Body") :
                        new NotFoundException("Not Found")));

但没有效果。只有将@ExceptionHandler指定为RuntimeException.class时才能正常返回自定义响应体,否则只会返回Spring默认的错误信息。请问我哪里操作有误?

问题分析与解决方案

1. 异常处理方法的返回值类型不匹配

你的handleBadRequest方法声明返回BankFilteringError,但实际实例化的是MyError;handleNotFound方法声明返回Mono<BankFilteringError>,实际返回的是Mono<MyError>。如果MyError不是BankFilteringError的子类,这种类型不兼容会导致Spring无法正确序列化响应体,直接返回默认错误。

修正方案:
统一返回值类型和实例类型,比如都用Mono<MyError>:

@ExceptionHandler({BadRequestException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Mono<MyError> handleBadRequest(BadRequestException ex) {
    MyError error = new MyError();
    error.setCode("400");
    error.setDescription("Invalid Request Body");
    error.setInstance("instance");
    error.setMessage(ex.getMessage()); // 直接用异常携带的具体信息更灵活
    return Mono.just(error);
}

@ExceptionHandler({NotFoundException.class})
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public Mono<MyError> handleNotFound(NotFoundException ex) {
    MyError error = new MyError();
    error.setCode("404");
    error.setDescription("404 NOT_FOUND");
    error.setInstance("instance");
    error.setMessage(ex.getMessage());
    return Mono.just(error);
}

2. 响应式场景下异常处理的适配问题

你的Controller返回的是Mono响应式类型,但handleBadRequest直接返回普通对象而非Mono,在WebFlux环境中,这种同步返回值无法被响应式框架正确处理,导致自定义响应体不生效。

修正方案:
所有异常处理方法统一返回响应式类型(Mono),和Controller的响应式风格保持一致,代码参考上面的修正示例。

3. 异常抛出的时机逻辑错误

你添加的switchIfEmpty写在flatMap之后,而request.getEntity() == null的判断应该提前处理——如果request.getEntity()是null,Mono.fromCallable里调用peopleFiltering.filter(null)可能直接抛出异常,或者result为空的逻辑根本走不到switchIfEmpty。

修正方案:
提前校验请求参数,在进入响应式流之前抛出异常:

@PostMapping(consumes = "application/json", value = "id")
public Mono<MyResponse> filtering(@Valid @RequestBody MyRequest request) {
    // 提前校验参数,直接抛出BadRequestException
    if (request.getEntity() == null) {
        return Mono.error(new BadRequestException("Invalid Request Body"));
    }
    return Mono.fromCallable(() -> peopleFiltering.filter(request.getEntity()))
            .flatMap(result -> result.map(MyResponseMapper::mapToResponse))
            // 当result为空时抛出NotFoundException
            .switchIfEmpty(Mono.error(new NotFoundException("Not Found")));
}

4. 确保@ControllerAdvice的生效范围

如果你的Controller和CustomExceptionHandler不在同一个包下,需要指定@ControllerAdvice的扫描范围,避免异常处理器不生效:

@ControllerAdvice(basePackages = "com.your.package.controller") // 替换成你的Controller所在包
public class CustomExceptionHandler {
    // ... 异常处理方法
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:35:55