Search code examples
springspring-mvcjacksonhateoas

Spring MVC ResponseBodyAdvice for add header in specify return type


I have a method in my Controller that return list of object . This method support paging result and add HttpHeaders.LINK to HttpHeader for support Pagination Discoverability. I be forced to add this lines for support that . I think maybe ControllerAdvice or ResponseAdvice support to do this in that . How can i check return of method? and if return type is QueryResult then add HttpHeader to that?

@RequestMapping(value = "/list", method = RequestMethod.GET)
    public ResponseEntity<QueryResult<Personel>> search(
            SearchOption searchOption)  {
        QueryResult<Personel> list=ipersonelService.search();
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(list, "/api/personel");
        return new ResponseEntity<>(list, headers, HttpStatus.OK);
    } 

this class generate Headers by Paging class

public static HttpHeaders generatePaginationHttpHeaders(QueryResult page, String baseUrl)
    throws URISyntaxException {

    HttpHeaders headers = new HttpHeaders();
    headers.add("X-Total-Count", "" + page.getTotalElements());
    String link = "";
    if ((page.getPageNumber() + 1) < page.getTotalPages()) {
        link = "<" + generateUri(baseUrl, page.getPageNumber() + 1, page.getPageSize()) + ">; rel=\"next\",";
    }
    // prev link
    if ((page.getPageNumber()) > 0) {
        link += "<" + generateUri(baseUrl, page.getPageNumber() - 1, page.getPageSize()) + ">; rel=\"prev\",";
    }
    // last and first link
    int lastPage = 0;
    if (page.getTotalPages() > 0) {
        lastPage = page.getTotalPages() - 1;
    }
    link += "<" + generateUri(baseUrl, lastPage, page.getPageSize()) + ">; rel=\"last\",";
    link += "<" + generateUri(baseUrl, 0, page.getPageSize()) + ">; rel=\"first\"";
    headers.add(HttpHeaders.LINK, link);
    return headers;
}


private static String generateUri(String baseUrl, int page, int size) throws URISyntaxException {
        return UriComponentsBuilder.fromUriString(baseUrl).queryParam("page", page).queryParam("size", size).toUriString();
    }



public class QueryResult<T> {

    private int     pageNumber;
    private int     totalElements;
    private int     totalPages;
    private int     pageSize;

    @JsonProperty("rows")
    private List<T> items;

    public QueryResult() {

    }   

    public int getPageNumber() {
        return pageNumber;
    }

    public void setPageNumber(int pageNumber) {
        this.pageNumber = pageNumber;
    }

    public int getTotalElements() {
        return totalElements;
    }

    public void setTotalElements(int totalElements) {
        this.totalElements = totalElements;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public List<T> getItems() {
        return items;
    }

    public void setItems(List<T> items) {
        this.items = items;
    }

    public int getTotalPages() {
        if (this.getPageSize() > 0)
            return this.getTotalElements() / this.getPageSize();

        return 0;
    }

    public void setTotalPages(int totalPages) {
        this.totalPages = totalPages;
    }

}

i want to write method like this and automatic add link headers

@RequestMapping(value = "/list", method = RequestMethod.GET)
        public QueryResult<Personel> search(
                SearchOption searchOption)  {
            return ripersonelService.search();
        } 

after search i found this solution

@ControllerAdvice
public class BaseController implements ResponseBodyAdvice<Object> {


    private final Logger logger = Logger.getLogger(BaseController.class);

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(
            Object body,
            MethodParameter returnType,
            MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType,
            ServerHttpRequest request,
            ServerHttpResponse response)  {
        if(body instanceof QueryResult){
            QueryResult result =(QueryResult) body;
            HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(result, request.getURI().getPath());
            response.getHeaders().add(HttpHeaders.LINK, headers.getFirst(HttpHeaders.LINK));
            response.getHeaders().add("X-Total-Count", headers.getFirst("X-Total-Count"));
        }

        return body;
    }
}

Solution

  • You can use an implementation of ResponseBodyAdvice (typically also annotated with @ControllerAdvice). Your implementation's beforeBodyWrite method will be called with the response body and the ServerHttpResponse. The latter gives you access to the response headers and allows you to update them as appropriate.