4
votes

I want to implement pagination for my REST API. The application uses Spring Hateoas 0.8, Spring MVC 3.2.4 and Spring Integration 2.2.4. I see that Spring Hateoas has the PagedResources class but this does not split up the results into pages. It seems that spring-data is required to have properly created pages with previous and next links correctly created.

I have tried this but the links are not correct and all results are displayed:

    @RequestMapping(value = "/resources", method = RequestMethod.GET, produces="application/json")
public HttpEntity<Resources<RequestResource>> showAllAsResources() {
    Iterable<? extends Request> people = requestAccess.getRequests();
    RequestAssembler assembler = new RequestAssembler();
    List<RequestResource> resources = assembler.toResources(people);
    Resources<RequestResource> wrapped = new Resources<RequestResource>(resources, linkTo(RequestsController.class)
            .withSelfRel());
    return new HttpEntity<Resources<RequestResource>>(wrapped);
}

All google searches seem to point to spring-data. I can't use spring-data as I get my data from spring-integration which calls a soap web service and it returns all results for a search. I did try adding spring-data classes:

@RequestMapping(value = "/resources2", method = RequestMethod.GET, produces="application/json")
public HttpEntity<PagedResources<Resource<RequestResource>>> viewResources()
{
    List<Request> requests = requestAccess.getRequests2();
    Page page = new PageImpl<Request>(requests, new PageRequest(0, 5), requests.size());

    RequestAssembler requestAssembler = new RequestAssembler();

    return new ResponseEntity<PagedResources<Resource<RequestResource>>>(pagedResourcesAssembler.toResource(page, requestAssembler), HttpStatus.OK);        
}

This presents the response below which seems to have correct links but all data is presented and not just the page data:

{
"links": [
{
"rel": "next",
"href": "http://lwss:9084/lerest/v1/resources2?page=2&size=5"
},
{
"rel": "prev",
"href": "http://lwss:9084/lerest/v1/resources2?page=0&size=5"
}
],
"content": [
{
"givenName": "Joe",
"familyName": "Bloggs101",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/101"
}
]
},
...
{
"givenName": "Joe",
"familyName": "Bloggs200",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/101"
}
]
},
]
}
],
"page": {
"size": 5,
"totalElements": 100,
"totalPages": 20,
"number": 1
}
}

Is there any Spring classes that can help me achieve this or do I need to implement my own paging?

Many thanks, Martin

UPDATE

Using the solution from Custom pagination by array list the following code produces the response below, though I think this is a bit of overkill. Are there any Spring helper classes that could achieve the same thing?

@RequestMapping(value = "/resources1", method = RequestMethod.GET, produces="application/json")
public HttpEntity<PagedResources<Resource<RequestResource>>> viewResources1(Pageable pageable)
{   
    int page = pageable.getPageNumber();
    int count = pageable.getPageSize();
    RequestAssembler requestAssembler = new RequestAssembler();

    List<Request> requests = requestAccess.getRequests2();
    int max = (count*(page+1)>requests.size())? requests.size(): count*(page+1);

    Page<Request> pageRequests = new PageImpl<Request>(requests.subList(page*count, max), pageable, requests.size());

    return new ResponseEntity<PagedResources<Resource<RequestResource>>>(pagedResourcesAssembler.toResource(pageRequests, requestAssembler), HttpStatus.OK);
}

    {
"links": [
{
"rel": "next",
"href": "http://lwss:9084/lerest/v1/resources1?page=4&size=20"
},
{
"rel": "prev",
"href": "http://lwss:9084/lerest/v1/resources1?page=2&size=20"
}
],
"content": [
{
"givenName": "Joe",
"familyName": "Bloggs161",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/161"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs162",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/162"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs163",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/163"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs164",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/164"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs165",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/165"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs166",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/166"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs167",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/167"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs168",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/168"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs169",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/169"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs170",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/170"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs171",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/171"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs172",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/172"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs173",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/173"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs174",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/174"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs175",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/175"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs176",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/176"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs177",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/177"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs178",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/178"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs179",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/179"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs180",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/180"
}
]
}
],
"page": {
"size": 20,
"totalElements": 100,
"totalPages": 5,
"number": 3
}
}
Sounds like you'd like to use the advantages of spring-data-rest without and actual repository "behind" it. That's what this question is about: stackoverflow.com/questions/35554297/… - msp
Not exactly, I'm already using spring-hateoas and have a list of items from spring integration. I simply want to have those 'converted' to paged objects. Is that possible? - Martin Donaghy
Maybe I didn't understand the question but it looks like you are sending all data to the client and now you want to know why all data is sent to the client. - a better oliver
I'm obviously not using the paging classes correctly. They are able to display the meta data and links correctly on the client but all of the data is sent instead of one particular page's data. Does that make sense? - Martin Donaghy
Is the Page type supposed to hold all the data or only a slice of the data, i.e. page size is 5 - will there only be 5 items? - Martin Donaghy