Search code examples
javaspringspring-bootpaginationspring-webclient

Sort passed in Pageable to Spring controller appends direction to property name, causing a SQL error


I need to call a pageable endpoint in my Spring Boot app from another Spring Boot app. I'm trying to pass the pageable options through from the first app to the second, but I'm having an issue where the property name comes in as firstName: ASC. When the direction is appended to that, it becomes firstName: ASC: ASC which causes the JPA query to throw an exception.

What's the proper way to pass the pageable options from my first endpoint to my second?

Calling app

@GetMapping("/v1/users")
    public Flux<User> getUsersByAccount(@RequestParam Long accountId,
                                        @PageableDefault(size = 10, sort = "firstName") Pageable pageable) {
        return userService.getUsersByAccount(accountId, pageable);
}
public Flux<User> getUsersByAccount(Long accountId, Pageable pageable) {
    int page = pageable.getPageNumber();
    int size = pageable.getPageSize();
    Sort sort = pageable.getSort();

    return webClient.backendService()
        .get().uri(builder -> builder
            .path("/rest/users")
            .queryParam("accountId", accountId)
            .queryParam("page", page)
            .queryParam("size", size)
            .queryParam("sort", sort)
            .build())
        .retrieve()
        .bodyToFlux(ContactInfo.class);
}

I am splitting the Pageable out into its components because I wasn't sure how to pass the whole object at once since it's not a named parameter in the second app. Note that at this point, the sort looks fine and appears as it should with firstName and ASC as separate values for property name and direction, respectively.

Called app

@GetMapping("/rest/users")
    public List<User> getUsersByAccount(@RequestParam Long accountId, Pageable pageable) {
        return userService.getUsersByAccount(accountId, pageable);
}

Solution

  • As @M. Deinum mentioned, Sort's toString() doesn't produce a representation that can be directly serialized back into a Sort object, (and there is no such method on the object to do that).

    You can convert it to the proper form like this:

    List<String> sorts = new ArrayList<>();
    sort.forEach(order -> sorts.add(String.join(",", order.getProperty(), order.getDirection().toString())));
    builder.queryParam("sort", sorts.toArray());
    

    This produces the correct representation of ["propertyName,direction"].