评论

收藏

[Java] Spring中的REST分页的实现代码

编程语言 编程语言 发布于:2021-10-05 17:04 | 阅读数:242 | 评论:0

本文将介绍在REST API中实现分页的基础知识。我们将专注于使用Spring Boot和Spring Data 在Spring MVC中构建REST分页,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
本文将介绍在rest api中实现分页的基础知识。我们将专注于使用spring boot和spring data 在spring mvc中构建rest分页。
分页是一种处理大结果数据集的机制。在rest api中实现分页并没有什么不同,但需要一些额外的思考过程。为rest api提供流畅有效的分页可以增加用户体验并有助于构建高效,快速的rest api。我们使用spring boot作为示例。
1.资源与表示
在我们开始设计分页api之前,我们需要清楚地了解页面作为资源或资源的表示。我们需要记住许多基本要素
一个页面page不是rest中的一个资源,而是其请求的属性。
以资源名称product为构建分页的例子,在高层次上我们确实有以下三个选项来构建分页。

  • 将产品product作为资源并使用查询字符串来处理分页以及其他参数,例如排序等(例如http://domainname/products?page=1)。
  • 第二个选项是将页面page用作资源和查询字符串进行排序。(例如http://domainname/products/page/1?sort_by=date)。
  • 使用页面page作为资源和url部分进行排序。(例如http://domainname/products/date/page/1)
考虑到上述问题,让我们尝试回答一些在设计rest api分页时有用的问题。

  • 您是否将页面page视为页面中产品的资源?
请记住,rest api不是围绕任何预定义的规则或规范构建的,所有上述三个选项都是有效的,并且基于上述问题的答案。如果我们将页面视为资源,则选项3是有效选择;但如果我们说页面上的产品是资源,那么选项3不再有效(在第1,2页上的产品可能会在将来更改),就个人而言,我会选择选项1,因为对我来说,页面 page 不是  资源resouce,它是请求的属性。
2.可发现性
可发现性  有助于使  restful api  更加实用和优雅。使rest api  可被发现经常被忽视。以下是rest api可发现性的高级摘要 。

  • 有了这个功能,rest api在对客户端的响应中提供完整的uri意味着没有客户端需要“组合”uri。
  • 客户端api独立于uri结构。
  • 通过以上2点,api更加灵活,允许开发人员在不破坏api的情况下更改uri架构。(请记住,api提供所有uri,它们不是由客户端api动态创建的)。
可发现性与rest api中的hateoas密切相关。rest api分页可发现将通过"next","previous","first"和"last"链路作为响应数据的一部分。我们正在考虑如何在分页期间将此功能添加到您的api。
3.分页设计考虑因素
在构建rest api分页界面时,让我们快速介绍一些要点。
3.1 限制limit
限制允许api和客户端控制结果集中请求的结果数。通过传递  limit 参数,您可以指定每个页面要返回的项目数.api可以配置默认限制,但应允许客户端指定限制。
http://hostname/products?page=1&limit=50
在上面的请求中,客户端将限制设置为50.小心,同时允许客户将limit 参数设置 , 设置为极高数量的限制会降低api性能。建议在api设计期间具有最大允许限制。
3.2 排序
排序总是与搜索和分页并排。在设计rest api时,提供灵活性,让客户指定排序选项,同时从api返回结果。建议在设计api时使用  sort_by = [attribute  name] - [asc / desc]模式.api设计器应将允许的属性名称指定为sort参数。例如,您可以使用?name-asc按产品名称排序或?name-desc反向排序。
4. maven依赖
我们在spring中处理rest分页时介绍了所有基本内容。我们在这篇文章中使用了以下技术堆栈,但它可以在任何其他技术上实现,前提是您在设计时遵循所有基本原则。

  • spring boot
  • jpa.
  • spring data rest
在本文中使用spring data rest的原因之一是data rest api支持的开箱即用功能。
我们将在pom.xml中添加以下依赖项

  • spring boot jpa
  • spring boot data rest
  • hateos和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控制器:
@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 + """;
 }
}
让我们快速介绍上面代码中的几个要点。

  • 我们使用  pageable作为控制器的参数之一。这将有助于返回页面而不是列表。
  • pageable具有所有必需的分页信息。
  • pageable在spring jpa中运行得非常好,并且透明地处理分页。
4.2 previous 和next 链接
每个页面响应将返回链接到当前页面前面和后面的页,这是基于使用iana定义链接关系 prev 和  next。但是,如果您当前位于结果的第一页,则不会呈现任何 prev链接。
我们来看下面的例子:
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
  }
}
让我们深入了解响应数据中的一些有趣事实

  • next 链接指向下一页。last 链接指向的最后一个结果集。
  • self 链接提供整个系列。
  • 底部  page 提供有关分页的信息,包括页面大小,总结果,总页数和当前页码。
4.2使用链接头
http标头是rest api的关键方面.http链接标头还可用于将分页信息传递给客户端。通过上述测试,系统将返回以下附加信息作为link http标头的一部分。
link →<http://localhost:8080/products?page=0&size=20>; rel="first", <http://localhost:8080/products?page=1&size=20>; rel="next"
rel="next" 意思是下一页是  page=2;rel="first" 意思是第一页总是依赖page=2.于提供给你的这些链接关系。不要试图猜测或构建自己的url。spring pagedresource提供所有这些信息作为结果的一部分,我们只需要确保从这些信息中构建正确的http头。在我们的控制器示例中,我们在createlinkheader方法中构建标头。
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 + """;
}
总结
在这篇文章中,我们学习了如何在spring和spring boot中实现  rest分页。我们讨论了如何构建响应以及在rest api响应中使用链接http标头的重要性。
所有这些示例和代码片段的实现都可以在github项目中找到
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持CodeAE代码之家
原文链接:https://www.jdon.com/51623

关注下面的标签,发现更多相似文章