本文將介紹在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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<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控制器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
@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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
{ "_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標頭的一部分。
1
|
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方法中構建標頭。
1
2
3
4
5
6
7
8
9
10
11
|
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項目中找到
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://www.jdon.com/51623