Shanka Somasiri
Shanka Somasiri

Reputation: 631

@Pattern annotation is not validating the format

I have a projectDTO which accepts a project name and start date. See below

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProjectDTO {
    @NotBlank(message = "project name is required")
    private String projectName;

    @NotNull(message = "project start date is required")
    @Pattern(regexp = "^\\d{4}\\-(0[1-9]|1[012])\\-(0[1-9]|[12][0-9]|3[01])$", message = "date format should be yyyy-MM-dd")
    private LocalDate startDate;
}

Below is my ExceptionHandler class

@RestControllerAdvice
public class ApplicationExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    private ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {

        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.toList());

        ApiException apiException = ApiException.builder()
                .errors(errors)
                .httpStatus(HttpStatus.BAD_REQUEST)
                .timestamp(ZonedDateTime.now(ZoneId.of("Z")))
                .build();

        return new ResponseEntity<>(apiException, HttpStatus.BAD_REQUEST);
    }
}

If I add a null value for the project name it validates as expected and give the custom error response as below.

{
    "errors": [
        "project name is required"
    ],
    "httpStatus": "BAD_REQUEST",
    "timestamp": "2023-02-17T05:06:08.9362836Z"
}

But if I provide a wrong format for the start date (e.g. - 2023/12-17) the validation is not working. Getting below error

{
    "timestamp": "2023-02-17T05:21:07.004+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/api/projects"
}

In the console

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.LocalDate` from String "2023/02-17": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '2023/02-17' could not be parsed at index 4; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDate` from String "2023/02-17": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '2023/02-17' could not be parsed at index 4<LF> at [Source: (PushbackInputStream); line: 5, column: 25] (through reference chain: com.theravado.donationservice.dto.ProjectRequest["projects"]->java.util.ArrayList[0]->com.theravado.donationservice.dto.ProjectDTO["startDate"])]

Can you give my some input on how to get this date format validation can be fixed for @Pattern so that I can output an informative error message like in project name?

Cheers

Edited

Thanks @times29. I feel like my validation is not applied at all for the start date here. exquisitely meantioning that "date format should be yyyy-MM-dd" rather than JSON parse error: Cannot deserialize value of type java.time.LocalDate from String "2023/02-17"

 @Pattern(regexp = "^\\d{4}\\-(0[1-9]|1[012])\\-(0[1-9]|[12][0-9]|3[01])$", message = "date format should be yyyy-MM-dd")

Upvotes: 0

Views: 1125

Answers (2)

Shanka Somasiri
Shanka Somasiri

Reputation: 631

Actually the issue was @Pattern cannot be used for LocalDate. When I changed the type to String in my projectDTO this started working

@NotNull(message = "project start date is required")
    @Pattern(regexp = "^\\d{4}\\-(0[1-9]|1[012])\\-(0[1-9]|[12][0-9]|3[01])$", message = "date format should be yyyy-MM-dd")
    private String startDate;

Upvotes: 0

times29
times29

Reputation: 3373

You need a separate @ExceptionHandler for the HttpMessageNotReadableException. Your current @ExceptionHandler(MethodArgumentNotValidException.class) will only handle MethodArgumentNotValidException exceptions, but when a HttpMessageNotReadableException occurrs like in your case, it won't be handled by the handleMethodArgumentNotValid method.

    @ExceptionHandler(HttpMessageNotReadableException.class)
    private ResponseEntity<Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
        List<String> errors = ...; // Extract info from the exception
        ApiException apiException = ApiException.builder()
                .errors(errors)
                .httpStatus(HttpStatus.BAD_REQUEST)
                .timestamp(ZonedDateTime.now(ZoneId.of("Z")))
                .build();
        return new ResponseEntity<>(apiException, HttpStatus.BAD_REQUEST);
    }

Upvotes: 0

Related Questions