Jin Kwon
Jin Kwon

Reputation: 21978

How can I validate size of Pageable?

I'm using org.springframework.data.domain.Pageable with my @RestController.

How can I validate or limit the page size?

Without any validation, when clients call with size of 10000. The actual pageSize is 2000.

This could lead wrong signal for last page, I think.

How can I validate it and notify clients about it? Say with 400?

Upvotes: 8

Views: 6312

Answers (3)

Ostap
Ostap

Reputation: 67

You can simply use configuration

spring.data.web.pageable.max-page-size=400

This will not lead to any error if one tries to get 401 records. Only 400 will be returned. More on stuff that can be configured via spring properties could be found here:

https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html

Upvotes: 1

Manoj
Manoj

Reputation: 131

You can write a custom annotation to validate Pageable object

@Constraint(validatedBy = PageableValidator.class)
@Target( { ElementType.METHOD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface PageableConstraint {
    String message() default "Invalid pagination";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

    int maxPerPage() default 100;;
}

and Its implementation

public class PageableValidator implements
        ConstraintValidator<PageableConstraint, Pageable> {

    private int maxPerPage;

    @Override
    public void initialize(PageableConstraint constraintAnnotation) {
        maxPerPage=constraintAnnotation.maxPerPage();
    }

    @Override
    public boolean isValid(Pageable value, ConstraintValidatorContext context) {
        return value.getPageSize()<=maxPerPage;
    }
}

and you can use it over your controller like any other javax validation annotations.

@RestController
@RequestMapping("/api")
@Validated
public class EmployeeRestController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/employees")
    public List<Employee> getAllEmployees(@PageableConstraint(message = "Invalid page size",maxPerPage = 400) Pageable pageable) {
        return employeeService.getAllEmployees();
    }
}

Upvotes: 7

Sunil Dabburi
Sunil Dabburi

Reputation: 1472

Try using Custom Annotations to validate the Pageable object. If it is not working, try the argument resolver as shown below.

You can create Argument Resolver in your Spring Configuration that is extending WebMvcConfigurerAdapter

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver() {

        @Override
        public Pageable resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
            Pageable p = super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory);
            if (webRequest.getParameter("per_page") != null) {
                int pageSize = Integer.parseInt(webRequest.getParameter("per_page"));
                if (pageSize < 1 || pageSize > 2000) {
                    message = "Invalid page size";
                }
            }
            if (message != null) {
                Set<CustomConstraintViolation> constraintViolations = new HashSet<>();
                constraintViolations.add(new CustomConstraintViolationImpl(message));
                throw new ConstraintViolationException(constraintViolations);
            }
            return new PageRequest(p.getPageNumber(), p.getPageSize());
        }
    };
    resolver.setMaxPageSize(2000);
    argumentResolvers.add(resolver);
    super.addArgumentResolvers(argumentResolvers);
}

This resolver will make sure the max page size is 2000 for every request to your controller.

You need to throw a ConstraintViolationException when the size is more than 2000. For that you need to create a custom ConstraintViolation interface and implement it

public CustomConstraintViolation implements ConstraintViolation<CustomConstraintViolation> {}

public CustomConstraintViolationImpl implements CustomConstraintViolation {

...

}

Upvotes: 1

Related Questions