Spring Boot 显示无效日期(年月)格式的错误消息:例如 2020-15

Springboot show error message for invalid date (YearMonth) formats: eg 2020-15

我有一个带有 Spring 引导的项目,如果给定的日期格式不正确,我想显示错误响应。 正确的格式是 yyyy-MM (java.time.YearMonth),但我想在有人发送 2020-13、2020-111 或 2020-1 时显示一条消息。

当我添加自定义验证器时,调试器会进入那里并发出有效请求,但不会发出错误请求。我还尝试将 message.properties 与 typeMismatch.project.startdate=Please enter a valid date. 一起使用,但我也没有在响应正文中看到该消息。 应用程序似乎不理解我的错误请求,然后总是抛出一个空主体的 BAD REQUEST,这并不奇怪,因为它不是有效日期。

谁能解释一下如何在响应中针对这些不正确的值显示错误消息? 或者没有其他方法然后使用字符串并将其转换为 YearMonth 对象以便我可以显示 catch 并显示错误消息?

请求对象:

@Getter
@Setter    
public class Project {
    @NotNull(message = "mandatory")
    @DateTimeFormat(pattern = "yyyy-MM")
    private YearMonth startdate;
}

控制器:

@RestController
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public class ProjectController {

    @PostMapping(value = "/project", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Project newProject(@Valid @RequestBody Project newProject) {
        return projectService.newProject(newProject);
    }
}

异常处理程序:

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @SneakyThrows
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        headers.add("Content-Type", "application/json");

        ObjectMapper mapper = new ObjectMapper();

        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach(error -> {
            String name;
            if (error instanceof FieldError)
                name = ((FieldError) error).getField();
            else
                name = error.getObjectName();
            String errorMessage = error.getDefaultMessage();
            errors.put(name, errorMessage);
        });

        return new ResponseEntity<>(mapper.writeValueAsString(errors), headers, status);
    }
}

好的,我制定了一个适合我的解决方案。 我已经在下面添加了解决方案,以供日后发现此主题并遇到与我相同的问题的人使用。

使用简单的正则表达式模式创建自定义验证器:

@Target({ FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = YearMonthValidator.class)
@Documented
public @interface YearMonthPattern {

    String message() default "{YearMonth.invalid}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

}
public class YearMonthValidator implements ConstraintValidator<YearMonthPattern, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        Pattern pattern = Pattern.compile("^([0-9]{4})-([0-9]{2})$");
        Matcher matcher = pattern.matcher(value);
        try {
            return matcher.matches();
        } catch (Exception e) {
            return false;
        }
    }
}

更新请求对象:

@Getter
@Setter
public class Project {
    @NotNull(message = "mandatory")
    @YearMonthPattern
    private String startdate;
    
    public YearMonth toYearMonth(){
        return YearMonth.parse(startdate);
    }
}

DateTimeFormat 注释已替换为我们新的自定义验证器,而不是 YearMonth,将其设为字符串。现在可以执行验证器注解了,因为到 YearMonth 的映射不会再失败了。

我们还添加了一个新方法,在 Spring 验证请求正文后将 String startdate 转换为 YearMonth,因此我们可以在服务中将其用作 YearMonth,而不必每次都转换它.

现在当我们发送一个请求体时:

{
    "startdate": "2020-1"
}

我们收到一个不错的 400 错误请求,响应如下:

{
    "endDate": "{YearMonth.invalid}"
}