Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring auto-add 'X-Total-Count' header

I'm using 'admin-on-rest' UI for my Web-application and it has next restriction:

Note: The jsonServer REST client expects the API to include a X-Total-Count header in the response to GET_LIST calls. The value must be the total number of resources in the collection. This allows admin-on-rest to know how many pages of resources there are in total, and build the pagination controls.

I solved the problem by adding X-Total-Count header to my list-returning REST-endpoints manually like this: response.addHeader("X-Total-Count", String.valueOf(outputList.size()));

But I'm wondering: if there's some elegant way to do it automatically in Spring? I mean auto-add this header with proper value when some endpoint returns JSON-list?

like image 787
Dmytro Titov Avatar asked Jun 05 '17 18:06

Dmytro Titov


2 Answers

In case you don't want just the total number of the elements in the response but the total number of entities in the corresponding JPA method of PagingAndSortingRepository you can do something like that which is useful for paging applications :)

Inspired by Bohdan's answer ( https://stackoverflow.com/a/44376133/986160)

@ControllerAdvice
public class ResourceSizeAdvice implements ResponseBodyAdvice<Page<?>> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        //Checks if this advice is applicable.
        //In this case it applies to any endpoint which returns a page.
        return Page.class.isAssignableFrom(returnType.getParameterType());
    }

    @Override
    public Page<?> beforeBodyWrite(Page<?> page, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        serverHttpResponse.getHeaders().add("X-Total-Count",String.valueOf(page.getTotalElements()));
        return page;
    }

}

Make sure to call the Pageable version of the default methods like so it returns a Page and not a List:

repository.findAll(new PageRequest(0,100));

If you are not using Repositories then you have to do two queries: Select * from ... and Select count(*) from ... and return a Wrapper which has contents for the list of results plus total for the total coming from count. Then you can change the @ControllerAdvice class to expect your Wrapper and get the total from it and put it in the header

like image 164
Michail Michailidis Avatar answered Oct 01 '22 01:10

Michail Michailidis


Because from Page you can get total elements. So I've added headers to ResponseEntity

@GetMapping(value = POSTS, headers = "Accept=application/json")
public ResponseEntity<?> getListPost(@RequestParam(required = false, defaultValue = "0") Integer page, @RequestParam(required = false, defaultValue = "25") Integer size) {
    // Create pageable
    Pageable pageable = new PageRequest(page, size);
    Page<Post> pagePost = postService.getPagePost(pageable);

    HttpHeaders headers = new HttpHeaders() {
        {
            add("Access-Control-Expose-Headers", "Content-Range");
            add("Content-Range", String.valueOf(pagePost.getTotalElements()));
        }
    };
    //        return new ResponseEntity<>(new CommonResponseBody("OK", 200, postList), HttpStatus.OK);
    return new ResponseEntity<>(new CommonResponseBody("OK", 200, new LinkedHashMap() {
        {
            put("data", pagePost.getContent());
        }
    }), headers, HttpStatus.OK);
}

getPagePost is service method which uses Page findAll(Pageable pageable) in Repository.

Note: Change Content-Range to X-Total-Count if it doesn't work for you.

like image 42
Long Nguyen Avatar answered Oct 01 '22 01:10

Long Nguyen