Spring auto-add header "X-Total-Count"
I am using an "admin-on-rest" user interface for my web application and has the following limitation:
Note. The jsonServer REST client expects the API to include the X-Total-Count Header in response to GET_LIST calls. The value should be the total number of resources in the collection. This allows admin-on-rest to find out how many resource pages there are in total, and create pagination controls.
I solved the problem by manually adding the X-Total-Count header to my list-returning REST endpoints as follows:
response.addHeader("X-Total-Count", String.valueOf(outputList.size()));
But I'm wondering: If there is an elegant way to do this automatically in Spring? I mean automatically adding this header with the correct value when some endpoint returns a JSON list?
source to share
Yes there is! (If you are using spring 4.1 or higher).
It's called ResponseBodyAdvice and allows you to intercept calls (just before the response is written and gives you access to the raw HTTP response).
Basically, you need to implement a controller tip like this:
@ControllerAdvice
public class ResourceSizeAdvice implements ResponseBodyAdvice<Collection<?>> {
@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 collection.
return Collection.class.isAssignableFrom(returnType.getParameterType());
}
@Override
public Collection<?> beforeBodyWrite(Collection<?> body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
response.getHeaders().add("X-Total-Count", String.valueOf(body.size()));
return body;
}
}
source to share
If you don't just want the total number of items in the response, but the total number of objects in the corresponding JPA method PagingAndSortingRepository
, you can do something like this which is useful for swap applications :)
Inspired by Bogdan ( fooobar.com/questions/2414160 / ... )
@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 you call the default version of the default ones like this: Page
rather than List
:
repository.findAll(new PageRequest(0,100));
If you are not using repos, you need to run two queries:
Select * from ...
and
Select count(*) from ...
and return a Wrapper
which has the content for the list of results plus the total for the total coming from the invoice. Then you can change the class @ControllerAdvice
to expect yours Wrapper
and get the total from it and put it in the header
source to share
From behind the page, you can get total items. So I added headers to the 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 a service method that uses the findAll page (accessible page) in the repository.
Note. Change Content-Range to X-Total-Count if it doesn't work for you.
source to share