0

In a Spring-Web RestController we use PagedResources to create ResponseEntities. We have a RequestMethod that will trigger a background search for Elements in a 5km radius, if theres no result in 10km, 20km, until we find any Element.

Since we now alter the given Options from the Client (the Resource is called with a given distance), we want to give the altered searchDistance back to the Client. The actual Response then will be something like this:

{
  "_links": {
    "self": {
      "href": "http://...
      "templated": true
    }
  },
  "_embedded": {
    "parcelShopResourceList": [
      {
        "_id": "2760183530"
        ...
      },
      {
        "_id": "2760128505"
        ...
      },
      {
        "_id": "2760162267"
        ...
      },
      {
        "_id": "2760196914"
        ...
      },
      {
        "_id": "2760147255"
        ...
      }
    ]
  },
  "page": {
    "size": 200,
    "totalElements": 5,
    "totalPages": 1,
    "number": 0
    "searchDistance": 10
  }
}

Now the Question:

  • How can i extend PagedResourcesAssembler, so it will return any additional Page Informations?

I thought it would be enough to create an extended Page, so PagedResourcesAssemblerwill add the extra information to the page informations, but this was not the case.

ResponseEntity.ok(pagedResourcesAssembler.toResource(page, resourceAssembler));

I then tried to override the PagedResourcesAssembler itself, but since it uses a lot of private Methods the Override of PagedResourcesAssembler.createResource is not really usefull.

I think i am missing something completely. Can please anyone explain to me how i can add additional Information in my Response? I am sure there is a much more easy way for this.

Since i want to add an information that regards the whole List, i do not want to insert this information into a single resource just for convenience.

yarco
  • 172
  • 7
Micha
  • 1

2 Answers2

0

Indeed, a way to go is creating your own page resource and customize your entity as desired. This include the content to render and the meta data such as search distance, size and so on.

Here is an exemple :

 /**
 * 
 * Pagination for a resource content
 *
 * @param <T> content to render 
 */
public class PagedResource<T> {
  private List<T> content;
  private final Map metadata;
  public PagedResource(Page<T> page, String pageParam, String sizeParam, String sortParam, String sortField) {
    super();
    this.setContent(page.getContent());
    this.metadata = new HashMap();
    List<Link> links = new ArrayList<Link>();
    if (page.hasPrevious()) {
      String path = createBuilder().queryParam(pageParam, page.getNumber() - 1).queryParam(sizeParam, page.getSize()).queryParam(sortParam, sortField).build().toUriString();
      Link previousPageLink = new Link(path, Link.REL_PREVIOUS);
      links.add(previousPageLink);
    }
    if (page.hasNext()) {
      String path = createBuilder().queryParam(pageParam, page.getNumber() + 1).queryParam(sizeParam, page.getSize()).queryParam(sortParam, sortField).build().toUriString();
      Link nextPageLink = new Link(path, Link.REL_NEXT);
      links.add(nextPageLink);
    }
    if (!page.isFirst()) {
      Link firstPageLink = buildPageLink(pageParam, 0, sizeParam, page.getSize(), sortParam, sortField, Link.REL_FIRST);
      links.add(firstPageLink);
    }
    if (!page.isLast()) {
      int lastPage = page.getTotalPages() - 1;
      Link lastPageLink = buildPageLink(pageParam, lastPage, sizeParam, page.getSize(), sortParam, sortField, Link.REL_LAST);
      links.add(lastPageLink);
    }
    Link currentPagelink = buildPageLink(pageParam, page.getNumber(), sizeParam, page.getSize(), sortParam, sortField, Link.REL_SELF);
    links.add(currentPagelink);
    populateMetadata(page, links);
  }
  private void populateMetadata(Page<T> page, List<Link> links) {
    int per_page = page.getSize();
    int totalPages = page.getTotalPages();
    int numberOfPageElements = page.getNumberOfElements();
    metadata.put("numberOfPageElements", numberOfPageElements);
    metadata.put("perPage", per_page);
    metadata.put("totalPages", totalPages);
    metadata.put("links", links);
  }
  private Link buildPageLink(String pageParam, int page, String sizeParam, int size, String sortParam, String sortAttribute, String rel) {
    String path = createBuilder().queryParam(pageParam, page).queryParam(sizeParam, size).queryParam(sortParam, sortAttribute).toUriString();
    Link link = new Link(path, rel);
    return link;
  }
  private ServletUriComponentsBuilder createBuilder() {
    return ServletUriComponentsBuilder.fromCurrentRequestUri();
  }
  public List<T> getContent() {
    return content;
  }
  public void setContent(List<T> content) {
    this.content = content;
  }
  public Map getMetadata() {
    return metadata;
  }
}

You can find below a complete example of how to use custom paged resources.

Hassam Abdelillah
  • 2,246
  • 3
  • 16
  • 37
0

Thanks for the Example Hassam, but i was more or less bound to the existing response structure provided by hateoas!

I actually solved the Problem by creating a wrapper in the RestController that creates a new Response and adds the needed Information around the Response created by PagedResourcesAssembler.

Micha
  • 1