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

Spring Boot:handleMethodArgumentNotValid对日期格式不匹配失效问题

问题描述

我有一个异常处理器类,对普通校验失败场景能正常工作,但handleMethodArgumentNotValid方法在dateOfBirth字段日期格式不匹配时不会被调用,仅在@Past校验失败时可正常触发。请问该如何处理日期格式不匹配异常并自定义提示信息?

现有代码

异常处理器类

package com.example.demo.exception.handler;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import com.example.demo.exception.ExceptionResponse;
import com.example.demo.exception.BadRequestException;

@ControllerAdvice
@RestController
public class ResponseExceptionHandler extends ResponseEntityExceptionHandler {
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex,
            HttpHeaders headers,
            HttpStatus status,
            WebRequest request) {
        ExceptionResponse response = new ExceptionResponse(ex.getBindingResult().toString(), request.getDescription(false));
        return new ResponseEntity(response, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler
    public final ResponseEntity<Object> handleAllExceptions(
            Exception ex,
            WebRequest request){
        ExceptionResponse response = new ExceptionResponse(ex.getMessage(), request.getDescription(false));
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }
}

User模型类

package com.example.demo.model;

import lombok.Data;
import java.util.Date;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.format.annotation.DateTimeFormat;

@Data
public class User {
    @Email(message = "邮箱格式无效", regexp = "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])")
    @JsonProperty("email")
    private String email;

    @Size(min = 2, message = "名字长度至少为2个字符")
    @NotNull
    @JsonProperty("first_name")
    private String firstName;

    @JsonProperty("last_name")
    private String lastName;

    @JsonProperty("user_uuid")
    private String userUUID;

    @NotNull(message = "密码不能为空")
    @JsonProperty("password")
    private String password;

    @DateTimeFormat(pattern = "yyyy-mm-dd")
    @JsonFormat(pattern = "mm/dd/yyyy")
    @JsonProperty("date_of_birth")
    @Past(message = "出生日期必须是过去的日期")
    private Date dateOfBirth;
}
解决方案

1. 明确异常触发逻辑

日期格式不匹配时,参数绑定/JSON解析阶段就会失败,根本到不了@Valid校验环节,因此不会触发MethodArgumentNotValidException。根据请求类型不同,会抛出两种异常:

  • JSON请求:Jackson解析失败抛出HttpMessageNotReadableException
  • 表单请求:参数类型不匹配抛出MethodArgumentTypeMismatchException

2. 修正日期注解的格式错误

你的@DateTimeFormat和@JsonFormat里的mm代表分钟,月份应该用MM,否则格式校验逻辑完全错误:

@DateTimeFormat(pattern = "yyyy-MM-dd") // 修正mm为MM
@JsonFormat(pattern = "MM/dd/yyyy", timezone = "GMT+8") // 修正mm为MM,可选添加时区避免解析偏差
@JsonProperty("date_of_birth")
@Past(message = "出生日期必须是过去的日期")
private Date dateOfBirth;

3. 更新异常处理器,添加对应异常处理

修改ResponseExceptionHandler,新增针对上述两种异常的处理逻辑,自定义提示信息:

package com.example.demo.exception.handler;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.http.converter.HttpMessageNotReadableException;

import com.example.demo.exception.ExceptionResponse;
import com.example.demo.exception.BadRequestException;

@ControllerAdvice
@RestController
public class ResponseExceptionHandler extends ResponseEntityExceptionHandler {

    // 处理@Valid校验失败的场景
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex,
            HttpHeaders headers,
            HttpStatus status,
            WebRequest request) {
        // 提取具体的字段校验错误信息,替换原有的BindingResult.toString()
        StringBuilder errorMsg = new StringBuilder();
        ex.getBindingResult().getFieldErrors().forEach(error -> 
            errorMsg.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("; ")
        );
        ExceptionResponse response = new ExceptionResponse(errorMsg.toString(), request.getDescription(false));
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    // 处理JSON请求中日期格式不匹配的异常
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, WebRequest request) {
        String errorMsg;
        // 判断是否为日期解析错误
        if (ex.getCause() instanceof InvalidFormatException) {
            InvalidFormatException cause = (InvalidFormatException) ex.getCause();
            if (cause.getTargetType().isAssignableFrom(Date.class)) {
                errorMsg = "date_of_birth字段格式错误,要求格式为MM/dd/yyyy";
            } else {
                errorMsg = "请求参数格式错误";
            }
        } else {
            errorMsg = "请求体解析失败";
        }
        ExceptionResponse response = new ExceptionResponse(errorMsg, request.getDescription(false));
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    // 处理表单请求中日期格式不匹配的异常
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<Object> handleMethodArgumentTypeMismatch(MethodArgumentTypeMismatchException ex, WebRequest request) {
        String errorMsg;
        if (ex.getRequiredType().isAssignableFrom(Date.class)) {
            errorMsg = "date_of_birth字段格式错误,要求格式为yyyy-MM-dd";
        } else {
            errorMsg = String.format("参数%s格式错误,要求类型为%s", ex.getName(), ex.getRequiredType().getSimpleName());
        }
        ExceptionResponse response = new ExceptionResponse(errorMsg, request.getDescription(false));
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request){
        ExceptionResponse response = new ExceptionResponse(ex.getMessage(), request.getDescription(false));
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }
}

4. 注意事项

  • 确保引入Jackson的InvalidFormatException类:com.fasterxml.jackson.databind.exc.InvalidFormatException
  • 若仅处理JSON请求,可只保留HttpMessageNotReadableException的处理方法;若仅处理表单请求,保留MethodArgumentTypeMismatchException即可
  • 自定义的ExceptionResponse需能正常序列化返回给前端

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 15:24:21