Implementation code for REST paging in Spring

  • 2021-06-28 09:18:56
  • OfStack

This article introduces the basics of paging in REST API.We will focus on building REST pages in Spring MVC using Spring Boot and Spring Data.

Paging is a mechanism for processing large result datasets.Paging in REST API is not different, but it requires some extra thought.Providing smooth and effective paging for REST API can increase the user experience and help build an efficient and fast REST API.We use Spring Boot as an example.

1. Resources and Representation

Before we start designing the page API, we need to have a clear understanding of the page as a representation of a resource or resource.There are many basic elements that we need to remember

A page, Page, is not a resource in REST, but a property of its request.

Using the resource name Product as an example of building paging, we do have three options at a high level to build paging.

Use the product Product as a resource and query strings to handle paging and other parameters, such as sorting (e.g. http://domainname/products?page=1). The second option is to sort the page Page as a resource and a query string. (For example, http://domainname/products/page/1 ?sort_by=date). Sort the resources and URL sections using page Page. (For example, http://domainname/products/date/page/1)

Given these questions, let's try to answer a few useful questions when designing REST API paging.

Do you view page Page as a resource for products on the page ?

Keep in mind that REST API is not built around any predefined rules or specifications, all three options above are valid and based on the answers to the questions above.Option 3 is a valid choice if we treat the page as a resource;But if we say that the product on the page is a resource, then option 3 is no longer valid (the product on page 1 and 2 may change in the future). Personally, I would choose option 1, because for me, page Page is not a resource Resouce, it is a requested property.

2. Discoverability

Discoverability helps make RESTful API more useful and elegant.Make REST API discoverable and often overlooked.Below is a high-level summary of REST API discoverability.

With this feature, REST API provides a complete URI in its response to the client, meaning that no client needs to "assemble" URI. The client API is independent of the URI structure. With these two points, API is more flexible, allowing developers to change the URI architecture without breaking API. (Keep in mind that API provides all URIs, which are not dynamically created by the client API.

Discoverability is closely related to HATEOAS in REST API.The REST API paging finds that the response data will be part of "next", "previous", "first" and "last" links.We are considering how to add this feature to your API during paging.

3. Paging Design Considerations

Let's take a quick look at a few key points when building the REST API paging interface.

3.1 Limit limit

Limit the number of results that API and the client are allowed to control in the result set.By passing the limit parameter, you can specify the number of items to return per page. API can configure default limits, but should allow clients to specify limits.

http://hostname/products?page=1 & limit=50

In the above request, the client set the limit to 50.Be careful, while allowing the client to set the limit parameter, a very high number of limits can degrade API performance.It is recommended that there be a maximum allowable limit during the API design.

3.2 Sorting

Sorting is always side by side with search and paging.When designing REST API, it provides flexibility for customers to specify sorting options and return results from API.It is recommended that sort_be used when designing APIby = [attribute name] - [asc / desc] mode. The API designer should specify the allowable attribute name as the sort parameter.For example, you can use ?name-asc is sorted by product name or ?name-desc reverse sort.

4. Maven dependency

We covered all the basics when dealing with REST paging in Spring.The following technology stack is used in this article, but it can be implemented in any other technology provided that you follow all the basic principles in your design.

Spring Boot JPA. Spring Data REST

One reason for using Spring Data REST in this article is the out-of-the-box functionality supported by Data REST API.

We will add the following dependencies to pom.xml

Spring Boot JPA Spring Boot Data REST HATEOS and Web

<dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-rest</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-hateoas</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
   <groupId>org.hsqldb</groupId>
   <artifactId>hsqldb</artifactId>
   <scope>runtime</scope>
  </dependency>
</dependencies>

4.1 REST Controller:


@RestController
public class ProductRESTController {

 @Autowired
 private ProductService productService;

 @Autowired private EntityLinks links;

 @GetMapping(value = "/products", produces = MediaType.APPLICATION_JSON_VALUE)
 public ResponseEntity < PagedResources < ProductEntity >> AllProducts(Pageable pageable, PagedResourcesAssembler assembler) {
 Page < ProductEntity > products = productService.findAllProducts(pageable);
 PagedResources < ProductEntity > pr = assembler.toResource(products, linkTo(ProductRESTController.class).slash("/products").withSelfRel());
 HttpHeaders responseHeaders = new HttpHeaders();
 responseHeaders.add("Link", createLinkHeader(pr));
 return new ResponseEntity < > (assembler.toResource(products, linkTo(ProductRESTController.class).slash("/products").withSelfRel()), responseHeaders, HttpStatus.OK);
 }

 private String createLinkHeader(PagedResources < ProductEntity > pr) {
 final StringBuilder linkHeader = new StringBuilder();
 linkHeader.append(buildLinkHeader(pr.getLinks("first").get(0).getHref(), "first"));
 linkHeader.append(", ");
 linkHeader.append(buildLinkHeader(pr.getLinks("next").get(0).getHref(), "next"));
 return linkHeader.toString();
 }

 public static String buildLinkHeader(final String uri, final String rel) {
 return "<" + uri + ">; rel=\"" + rel + "\"";
 }
}

Let's take a quick look at some of the points in the code above.

We use Pageable as parameter 1 of the controller.This will help you return to the page instead of the list. Pageable has all the necessary paging information. Pageable works very well in Spring JPA and handles paging transparently.

4.2 Previous and Next links

Each page response returns the page that links to the front and back of the current page, based on defining the link relationships prev and next using IANA.However, if you are currently on page 1 of the results, no prev links will appear.

Let's look at the following examples:

curl http://localhost:8080/products


{
  "_embedded": {
    "productEntities": [
      ...data...
    ]
  },
  "_links": {
    "first": {
      "href": "http://localhost:8080/products?page=0&size=20"
    },
    "self": {
      "href": "http://localhost:8080/products"
    },
    "next": {
      "href": "http://localhost:8080/products?page=1&size=20"
    },
    "last": {
      "href": "http://localhost:8080/products?page=4&size=20"
    }
  },
  "page": {
    "size": 20,
    "totalElements": 100,
    "totalPages": 5,
    "number": 0
  }
}

Let's dig deeper into some interesting facts in the response data

The next link points to the next page.The last result set that the last link points to. The self link provides the entire series. The bottom page provides information about paging, including page size, summary results, total number of pages, and current page number.

4.2 Use link headers

The HTTP header is a key aspect of REST API. The HTTP link header can also be used to pass paging information to clients.Through the above tests, the system will return the following additional information as part of the Link HTTP header.


Link  _ <http://localhost:8080/products?page=0&size=20>; rel="first", <http://localhost:8080/products?page=1&size=20>; rel="next"

rel="next" means that the next page is page=2;rel="first" means that page 1 always relies on page=2. For these link relationships provided to you.Don't try to guess or build your own URL.Spring PagedResource provides all this information as part of the result, and we just need to make sure that the correct HTTP header is built from it.In our controller example, we build headers in the createLinkHeader method.


private String createLinkHeader(PagedResources < ProductEntity > pr) {
 final StringBuilder linkHeader = new StringBuilder();
 linkHeader.append(buildLinkHeader(pr.getLinks("first").get(0).getHref(), "first"));
 linkHeader.append(", ");
 linkHeader.append(buildLinkHeader(pr.getLinks("next").get(0).getHref(), "next"));
 return linkHeader.toString();
}

public static String buildLinkHeader(final String uri, final String rel) {
 return "<" + uri + ">; rel=\"" + rel + "\"";
}

summary

In this article, we learned how to implement REST paging in Spring and Spring Boot.We discussed how to construct a response and the importance of using the linked HTTP header in an REST API response.

All these examples and snippet implementations can be found in the GitHub project


Related articles: