Spring的REST分頁

這是有關使用Spring 3.1和Spring Security 3.1和基于Java的配置來建立安全的RESTful Web Service的系列文章的第七篇。 本文將重點介紹RESTful Web服務中的分頁實現

REST with Spring系列:

  • 第1部分– 使用Spring 3.1和基于Java的配置引導Web應用程序
  • 第2部分– 使用Spring 3.1和基于Java的配置構建RESTful Web服務
  • 第3部分– 使用Spring Security 3.1保護RESTful Web服務
  • 第4部分– RESTful Web服務可發現性
  • 第5部分– 使用Spring進行REST服務發現
  • 第6部分– 使用Spring Security 3.1的RESTful服務的基本身份驗證和摘要身份驗證

頁面作為資源vs頁面作為表示

在RESTful架構的上下文中設計分頁時的第一個問題是將頁面視為實際資源還是僅表示資源 。 將頁面本身視為資源會帶來許多問題,例如不再能夠在調用之間唯一地標識資源。 這加上以下事實:在RESTful上下文之外,不能將頁面視為適當的實體,但是在需要時構造的所有者會使選擇變得簡單: 頁面是表示的一部分

在REST上下文中的分頁設計中的下一個問題是在何處包括分頁信息:

  • URI路徑中 :/ foo / page / 1
  • URI查詢/ foo?page = 1

請記住, 頁面不是資源 ,因此不再可以將頁面信息編碼為URI。

URI查詢中的頁面信息

URI查詢中對URI查詢中的頁面信息進行編碼是解決此問題的標準方法。 但是,這種方法確實有一個缺點 –它切入了用于實際查詢的查詢空間:

/ foo?page = 1&size = 10

控制器

現在,對于實現– 用于分頁的Spring MVC控制器非常簡單:

@RequestMapping( value = "admin/foo",params = { "page", "size" },method = GET )
@ResponseBody
public List< Foo > findPaginated( @RequestParam( "page" ) int page, @RequestParam( "size" ) int size, UriComponentsBuilder uriBuilder, HttpServletResponse response ){Page< Foo > resultPage = service.findPaginated( page, size );if( page > resultPage.getTotalPages() ){throw new ResourceNotFoundException();}eventPublisher.publishEvent( new PaginatedResultsRetrievedEvent< Foo >( Foo.class, uriBuilder, response, page, resultPage.getTotalPages(), size ) );return resultPage.getContent();
}

這兩個查詢參數在請求映射中定義,并通過@RequestParam注入到控制器方法中 HTTP響應和Spring UriComponentsBuilder注入到Controller方法中以包含在事件中,因為實現可發現性將需要兩者。

REST分頁的可發現性

分頁的范圍內,滿足RESTHATEOAS約束意味著使API的客戶端能夠基于導航中的當前頁面發現下一頁和上一頁。 為此,將使用Link HTTP標頭以及官方的 “ next ”,“ prev ”,“ first ”和“ last ”鏈接關系類型。

在REST中,可發現性是一個橫切關注點 ,不僅適用于特定操作,還適用于操作類型。 例如,每次創建資源時,客戶端應可發現該資源的URI。 由于此要求與ANY資源的創建有關,因此應分開處理并與主Controller流分離。

使用Spring,這種分離是通過事件來實現的 ,如上一篇文章中已充分討論的那樣,該文章側重于RESTful服務的可發現性。 對于分頁,在控制器中觸發了事件– PaginatedResultsRetrievedEvent –,并且在此事件的偵聽器中實現了可發現性:

void addLinkHeaderOnPagedResourceRetrieval( UriComponentsBuilder uriBuilder, HttpServletResponse response, Class clazz, int page, int totalPages, int size ){String resourceName = clazz.getSimpleName().toString().toLowerCase();uriBuilder.path( "/admin/" + resourceName );StringBuilder linkHeader = new StringBuilder();if( hasNextPage( page, totalPages ) ){String uriNextPage = constructNextPageUri( uriBuilder, page, size );linkHeader.append( createLinkHeader( uriForNextPage, REL_NEXT ) );}if( hasPreviousPage( page ) ){String uriPrevPage = constructPrevPageUri( uriBuilder, page, size );appendCommaIfNecessary( linkHeader );linkHeader.append( createLinkHeader( uriForPrevPage, REL_PREV ) );}if( hasFirstPage( page ) ){String uriFirstPage = constructFirstPageUri( uriBuilder, size );appendCommaIfNecessary( linkHeader );linkHeader.append( createLinkHeader( uriForFirstPage, REL_FIRST ) );}if( hasLastPage( page, totalPages ) ){String uriLastPage = constructLastPageUri( uriBuilder, totalPages, size );appendCommaIfNecessary( linkHeader );linkHeader.append( createLinkHeader( uriForLastPage, REL_LAST ) );}response.addHeader( HttpConstants.LINK_HEADER, linkHeader.toString() );
}

簡而言之,偵聽器邏輯檢查導航是否允許下一頁,上一頁,第一頁和最后一頁,如果允許,則將相關的URI添加到鏈接HTTP標頭中。 它還確保鏈接關系類型是正確的-“下一個”,“上一個”,“第一個”和“最后一個”。 這是偵聽器的唯一職責( 此處是完整代碼 )。

測試駕駛分頁

分頁和可發現性的主要邏輯都應由小型,集中的集成測試廣泛涵蓋; 與上一篇文章一樣 ,使用保證庫來使用REST服務并驗證結果。

這些是分頁集成測試的一些示例; 要獲得完整的測試套件,請查看github項目(本文結尾的鏈接):

@Test
public void whenResourcesAreRetrievedPaged_then200IsReceived(){Response response = givenAuth().get( paths.getFooURL() + "?page=1&size=10" );assertThat( response.getStatusCode(), is( 200 ) );
}
@Test
public void whenPageOfResourcesAreRetrievedOutOfBounds_then404IsReceived(){Response response = givenAuth().get( paths.getFooURL() + "?page=" + randomNumeric( 5 ) + "&size=10" );assertThat( response.getStatusCode(), is( 404 ) );
}
@Test
public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources(){restTemplate.createResource();Response response = givenAuth().get( paths.getFooURL() + "?page=1&size=10" );assertFalse( response.body().as( List.class ).isEmpty() );
}

測試駕駛分頁可發現性

測試分頁的可發現性相對簡單,盡管有很多基礎要講。 測試的重點是導航中當前頁面位置以及應該從每個位置發現的不同URI:

@Test
public void whenFirstPageOfResourcesAreRetrieved_thenSecondPageIsNext(){Response response = givenAuth().get( paths.getFooURL()+"?page=0&size=10" );String uriToNextPage = extractURIByRel( response.getHeader( LINK ), REL_NEXT );assertEquals( paths.getFooURL()+"?page=1&size=10", uriToNextPage );
}
@Test
public void whenFirstPageOfResourcesAreRetrieved_thenNoPreviousPage(){Response response = givenAuth().get( paths.getFooURL()+"?page=0&size=10" );String uriToPrevPage = extractURIByRel( response.getHeader( LINK ), REL_PREV );assertNull( uriToPrevPage );
} 
@Test
public void whenSecondPageOfResourcesAreRetrieved_thenFirstPageIsPrevious(){Response response = givenAuth().get( paths.getFooURL()+"?page=1&size=10" );String uriToPrevPage = extractURIByRel( response.getHeader( LINK ), REL_PREV );assertEquals( paths.getFooURL()+"?page=0&size=10", uriToPrevPage );
}
@Test
public void whenLastPageOfResourcesIsRetrieved_thenNoNextPageIsDiscoverable(){Response first = givenAuth().get( paths.getFooURL()+"?page=0&size=10" );String uriToLastPage = extractURIByRel( first.getHeader( LINK ), REL_LAST );Response response = givenAuth().get( uriToLastPage );String uriToNextPage = extractURIByRel( response.getHeader( LINK ), REL_NEXT );assertNull( uriToNextPage );
}

這些只是使用RESTful服務的集成測試的幾個示例。

獲取所有資源

關于分頁和可發現性的同一主題,必須選擇是否允許客戶端一次檢索系統中的所有資源 ,或者客戶端必須要求對它們進行分頁。

如果選擇了客戶端無法通過單個請求檢索所有資源,并且分頁不是可選的,而是必需的,則可以使用幾個選項來響應對“獲取所有”請求

一種選擇是返回404( 未找到并使用Link標頭使第一頁可被發現:

鏈接= <http:// localhost:8080 / rest / api / admin / foo?page = 0&size = 10>; rel =“ first ”,<http:// localhost:8080 / rest / api / admin / foo?page = 103&size = 10>; rel =“ 最后一個

另一個選擇是將重定向– 303( 請參閱其他返回到分頁的第一頁。

第三種選擇是為GET請求返回405( 不允許使用方法

帶有范圍HTTP標頭的REST Paginag

分頁的一種相對不同的方法是使用HTTP Range標頭Range,Content-Range,If-Range,Accept-Ranges –和HTTP狀態碼 – 206( 部分內容 ),413( 請求實體太大) ,416 ( 請求的范圍無法滿足 )。 關于這種方法的一種觀點是HTTP Range擴展不是用于分頁的,它們應該由服務器而不是由應用程序管理。

盡管在技術上不像本文中討論的實現那樣普遍,但是基于HTTP Range標頭擴展實現分頁還是可行的。

結論

本文介紹了使用Spring在RESTful服務中分頁的實現,并討論了如何實現和測試可發現性。 有關分頁的完整實現,請查看github項目。

如果您讀完本文, 則應 在Twitter上關注我 。

參考: Baeldung博客中我們JCG合作伙伴 Eugen Paraschiv的SpringREST分頁


翻譯自: https://www.javacodegeeks.com/2012/01/rest-pagination-in-spring.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/373283.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373283.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373283.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

眾籌源碼 php,助創cms眾籌源碼系統v1.0

什么是助創cms眾籌系統?使用“預約團購”的眾籌方式給自己的創意爭取大家的關注和支持&#xff0c;是近年來非常火熱的一種融資模式&#xff0c;助創cms眾籌系統可以10分鐘幫你打造一個和京東眾籌一樣的平臺&#xff0c;包含產品眾籌和公益眾籌兩個部分&#xff0c;可以直接拿…

Linq to SQL 的增刪改查操作

Linq&#xff0c;全稱Language Integrated Query&#xff0c;作為C#3.0新語法&#xff0c;是C#語言的一個擴展&#xff0c;可以將數據查詢直接集成到編程語言本身中。 Linq表達式和SQL語句差不多&#xff0c;說白了就是顛倒sql語法&#xff0c; from where select ...&#xff…

擴展您的JPA POJO

可擴展性是許多體系結構的重要特征。 它衡量是否容易&#xff08;或困難&#xff09; 它是在不影響現有核心系統功能的情況下添加或更改功能。 讓我們舉一個簡單的例子。 假設您的公司擁有一個核心產品來跟蹤體育俱樂部中的所有用戶。 在您的產品體系結構中&#xff0c;您有一個…

web框架--flask

flask介紹Flask是一個基于Python開發并且依賴jinja2模板和Werkzeug WSGI服務的一個微型框架&#xff0c;對于Werkzeug本質是Socket服務端&#xff0c;其用于接收http請求并對請求進行預處理&#xff0c;然后觸發Flask框架&#xff0c;開發人員基于Flask框架提供的功能對請求進行…

php spider shell,ScrapyShell使用

Scrapy ShellScrapy終端是一個交互終端&#xff0c;我們可以在未啟動spider的情況下嘗試及調試代碼&#xff0c;也可以用來測試XPath或CSS表達式&#xff0c;查看他們的工作方式&#xff0c;方便我們爬取的網頁中提取的數據。如果安裝了 IPython &#xff0c;Scrapy終端將使用 …

69 個經典 Spring 面試題和答案

Spring 概述 什么是spring?Spring 是個java企業級應用的開源開發框架。Spring主要用來開發Java應用&#xff0c;但是有些擴展是針對構建J2EE平臺的web應用。Spring 框架目標是簡化Java企業級應用開發&#xff0c;并通過POJO為基礎的編程模型促進良好的編程習慣。使用Spring框架…

高性能MySql

1、索引是對DB優化最有效的方式 varchar(10)定義的是字符的個數&#xff0c;如果是utf-8的話&#xff0c;最大是3X10個字節 二、索引類型 1、MySql的索引是在存儲引擎層實現的&#xff0c;各個存儲引擎的的索引方式也是不同的 2、B-Tree索引 MyISAM索引通過數據的物理位置引用被…

Java Swing井字游戲

大家好&#xff01; 哇&#xff0c;自從我在這里發布了東西以來已經有一段時間了&#xff01; 我必須說我真的很想寫東西&#xff0c;而且我保證我不會再陷入“作家的障礙”。 希望 ..最近兩個月發生了很多事情&#xff0c;我有很多話要說。 但是在這篇文章中&#xff0c;我只是…

Java小青蛙跳臺街,算法-青蛙跳臺階詳解

/*[跳臺階][題目]一只青蛙一次可以跳上1級臺階&#xff0c;也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。[解析]與斐波那契數列的求解過程類似。典型的動態規劃問題。對于第 n 級臺階&#xff0c;我們可以從第 n-1 級臺階一步到達&#xff0c;也可以從第 n-2 級…

apache服務器配置Net的實踐

前置&#xff1a; 在xp系統中&#xff0c;打補丁之類或啥子操作引起或多或少的問題&#xff0c;最終導致iis不能使用&#xff1b; 不想裝系統...忍著... 最近突發事件導致&#xff0c;需要摸一下apache服務器處理&#xff0c;好吧&#xff0c;那就搜索下吧..... 目標&#xff1…

TestNG或JUnit

多年以來&#xff0c;無論何時使用Java代碼進行單元測試&#xff0c;我始終會回到TestNG。 每當我拿起TestNG時&#xff0c;人們都問我為什么要繼續使用TestNG&#xff0c;尤其是默認開發環境&#xff08;例如Eclipse或Maven&#xff09;提供的JUnit時。 繼續進行同樣的戰斗&am…

event php,PHP event 事件機制

/** PHP 事件機制*/class baseClass{private $_e;public function __set($name,$value){if( strncasecmp($name,"on",2) 0 ){if(!isset($this->_e[$name]))$this->_e[$name] array();return array_push($this->_e[$name] , $value);}}public function __g…

Android JNI編程(五)——C語言的靜態內存分配、動態內存分配、動態創建數組...

版權聲明&#xff1a;本文出自阿鐘的博客&#xff0c;轉載請注明出處:http://blog.csdn.net/a_zhon/。 目錄(?)[] 一&#xff1a;什么是靜態內存什么又是動態內存呢&#xff1f; 靜態內存&#xff1a;是指在程序開始運行時由編譯器分配的內存&#xff0c;它的分配是在程序開始…

Visual Studio-C#-20160411

函數的四個要素包括&#xff1a;名稱&#xff0c;輸入&#xff0c;輸出&#xff0c;加工 注釋的方式&#xff1a;//只注釋一行&#xff1b;/**/注釋一段區域&#xff1b; namespace ConsoleApplication6 ---------//命名空間{ class Program ---------------------------//類…

配置MyBatis 3

MyBatis是一個非常流行且也是最有效的SQL映射框架。 MyBatis可用于Java和.net語言。 MyBatis并不是Hibernate的真正替代品&#xff0c;但是我們可以使用該框架來減少MyBatis提供的高效和高性能的數據庫相關代碼。 本教程將向您展示使用數據庫配置MyBatis 3的步驟。 MyBatis 3支…

php獲取src,PHP讀取文件

本文概述PHP提供了各種功能來從文件讀取數據。有多種功能允許你讀取所有文件數據, 逐行讀取數據以及逐字符讀取數據。下面提供了可用的PHP文件讀取功能。fread()fgets()fgetc()PHP讀取文件-fread()PHP fread()函數用于讀取文件的數據。它需要兩個參數&#xff1a;文件資源和文件…

HDOJ(HDU) 1406 完數

Problem Description 完數的定義&#xff1a;如果一個大于1的正整數的所有因子之和等于它的本身&#xff0c;則稱這個數是完數&#xff0c;比如6&#xff0c;28都是完數&#xff1a;6123&#xff1b;28124714。 本題的任務是判斷兩個正整數之間完數的個數。 Input 輸入數據包…

Allegro padstack

在ALLEGRO中&#xff0c;建立PCB封裝是一件挺復雜的事&#xff0c;而要建立FOOTPRINT&#xff0c;首先要有一個PAD&#xff0c;所以就要新建PADSTACK。 焊盤可以分兩種&#xff0c;表貼焊盤和通孔焊盤&#xff0c;表貼焊盤結構相對簡單&#xff0c;下面首先分析表貼焊盤的成分&…

java中datetime類型轉換,Java中日期格式和其他類型轉換詳解

涉及的核心類&#xff1a;Date類、SimpleDateFormat類、Calendar類一、Date型與long型Date型轉換為long型Date date new Date();//取得當前時間Date類型long date2long date.getTime();//Date轉longlong型轉換為Date型long cur System.currentTimeMills();//取得當前時間lon…

GWT MVP變得簡單

GWT Model-View-Presenter是用于大規模應用程序開發的設計模式。 它源于MVC&#xff0c;它在視圖和邏輯之間進行劃分&#xff0c;并有助于創建結構良好&#xff0c;易于測試的代碼。 為了幫助像我這樣的懶惰開發人員&#xff0c;我研究了如何減少使用聲明式UI時要編寫的類和接口…