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
相关产品推荐
相关产品推荐

