Search code examples
spring-bootrestparsingpageable

400 if Pageable cannot be parsed


Recently I was informed about a bug. The tester executed the following request:

curl -X 'GET' 'http://zones:26081/zones/vehicles-by-day?date=2023-05-20&page=0&size=10&sort=string'

And got 500 error in response:

{"type":"about:blank","title":"Internal Server Error","status":500,"detail":"No property 'string' found for type 'VehiclesByDayEntity'","instance":"/zones/vehicles-by-day"}

However she expected a 400 status which is reasonable. If org.springframework.data.domain.Pageable cannot be parsed it should be considered as a bad request.

Below is the excerpt from the @RestController code:

import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
...
@GetMapping(value = "/vehicles-by-day", produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<VehiclesByDay200Response> getVehiclesByDay(
        @Parameter(name = "date", description = "The date vehicles by day which for")
        @RequestParam(value = "date", required = false) LocalDate date,
        @Parameter(name = "pageable", in = ParameterIn.QUERY)
        @PageableDefault Pageable pageable
);

Is there a way to detect (intercept) the parsing error and return the 400 status instead of 500 ?

For example if I use a wrong date value in the request I'll get 400.

curl -X 'GET' 'http://zones:26081/zones/vehicles-by-day?date=2023-05-aa&page=0&size=10'
{"type":"about:blank","title":"Bad Request","status":400,"detail":"Failed to convert 'null' with value: '2023-05-aa'","instance":"/zones/vehicles-by-day"}

Why is it different with Pageable.


Solution

  • You can implement your own exception handler to do this. The exception that's being thrown is PropertyReferenceException. For example, you could write something like this:

    @ExceptionHandler(PropertyReferenceException.class)
    public ErrorResponse propertyReferenceExceptionHandler(PropertyReferenceException ex) {
        return ErrorResponse.create(ex, HttpStatus.BAD_REQUEST, ex.getMessage());
    }
    

    Either add this to your controller, or to a class annotated with @ControllerAdvice.