MethodArgumentNotValidException未在过滤器捕获及返回404问题排查
问题根源分析
1. 过滤器未捕获MethodArgumentNotValidException的原因
Spring请求处理流程中,过滤器(Filter)执行在DispatcherServlet之前。控制器层抛出的MethodArgumentNotValidException会被DispatcherServlet触发的全局异常处理器(@ControllerAdvice)捕获处理,不会向上传播到过滤器的catch块。过滤器的try-catch仅能捕获filter chain执行阶段(如请求预处理)抛出的异常,无法覆盖控制器层及后续环节的异常。
2. 全局异常处理后返回404的原因
全局异常处理器的handleValidationException方法返回JSON字符串,但Spring MVC会将该返回值视为视图名称,尝试转发到对应路径的资源。从日志可见,系统试图转发到以完整JSON字符串为路径的资源:
Forwarding to [{"statusCode":400,"status":"Bad Request",...}]
该路径不存在对应资源,因此触发NoResourceFoundException,最终返回404状态码。
解决方法
修复全局异常处理器
将异常处理方法改为直接返回错误对象,并通过@RestControllerAdvice或@ResponseBody让Spring直接序列化对象为JSON响应,而非当作视图名称处理:
方案一:使用@RestControllerAdvice替代@ControllerAdvice
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<GenericErrorRes> handleValidationException(MethodArgumentNotValidException ex) { String message = ex.getBindingResult().getFieldErrors().stream() .map(error -> error.getField() + ": " + error.getDefaultMessage()).findFirst().orElse(""); GenericErrorRes errorRes = new GenericErrorRes(); errorRes.setStatusCode(HttpStatus.BAD_REQUEST.value()); errorRes.setStatus(HttpStatus.BAD_REQUEST.getReasonPhrase()); errorRes.setErrorDesc(message); errorRes.setErrorCode(MNDT_PARAM_ERROR); errorRes.setMoreDetails(MNDT_PARAM_ERROR_MESSAGE); return new ResponseEntity<>(errorRes, HttpStatus.BAD_REQUEST); } }
方案二:保留@ControllerAdvice并添加@ResponseBody
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody public GenericErrorRes handleValidationException(MethodArgumentNotValidException ex) { // 同上构建errorRes的逻辑 return errorRes; } }
过滤器异常捕获补充说明
若需在过滤器层面处理控制器异常,无法通过直接捕获chain.doFilter的异常实现。可通过自定义DispatcherServlet或扩展HandlerExceptionResolver实现,但这类方案复杂度较高,不如直接完善全局异常处理器的实现更简洁。
另外,当前过滤器的try-catch块仅打印日志,未做后续响应处理,即使捕获到异常也无法生成正确响应,建议根据需求补充异常处理逻辑(如直接向response写入错误信息)。
内容的提问来源于stack exchange,提问作者Prathamesh Patle

