ODPOČINOK Stránkovanie na jar
Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:
>> SKONTROLUJTE KURZ1. Prehľad
Tento návod sa zameria na implementácia stránkovania v REST API pomocou Spring MVC a Spring Data.
2. Stránka ako zdroj vs Stránka ako reprezentácia
Prvou otázkou pri navrhovaní stránkovania v kontexte architektúry RESTful je, či je potrebné zohľadniť stránka skutočný zdroj alebo iba reprezentácia zdrojov.
Zaobchádzanie so samotnou stránkou ako s prostriedkom prináša množstvo problémov, ako napríklad nemožnosť jednoznačnej identifikácie zdrojov medzi hovormi. Toto, spolu so skutočnosťou, že vo vrstve perzistencie nie je stránka správna entita, ale držiteľ, ktorý je v prípade potreby konštruovaný, umožňuje jednoduchú voľbu: stránka je súčasťou znázornenia.
Ďalšia otázka v dizajne stránkovania v kontexte REST je kam zahrnúť informácie o stránkovaní:
- v ceste URI: / foo / page / 1
- dotaz URI: / foo? page = 1
Pamätajte na to stránka nie je zdrojom, kódovanie informácií o stránke v URI už nie je možné.
Použijeme štandardný spôsob riešenia tohto problému pomocou kódovanie stránkovacích informácií v dotaze URI.
3. Prevádzkovateľ
Teraz pre implementáciu - jarný radič MVC pre stránkovanie je priamy:
@GetMapping (params = {"page", "size"}) verejný zoznam findPaginated (@RequestParam ("page") int page, @RequestParam ("size") int size, UriComponentsBuilder uriBuilder, HttpServletResponse odpoveď) {Stránka resultPage = služba .findPaginated (stránka, veľkosť); if (page> resultPage.getTotalPages ()) {throw new MyResourceNotFoundException (); } eventPublisher.publishEvent (nový PaginatedResultsRetrievedEvent (Foo.class, uriBuilder, response, page, resultPage.getTotalPages (), veľkosť)); return resultPage.getContent (); }
V tomto príklade vkladáme dva parametre dotazu, veľkosť a stránka, v metóde Controller cez @RequestParam.
Prípadne sme mohli použiť a Stránkovateľné objekt, ktorý mapuje stránke, veľkosťa triediť parametre automaticky. Okrem toho PagingAndSortingRepository subjekt poskytuje hotové metódy, ktoré podporujú používanie Stránkovateľné ako parameter rovnako.
Injektujeme tiež Http Response a UriComponentsBuilder pomôcť s objaviteľnosťou - ktorú oddelíme prostredníctvom vlastnej udalosti. Ak to nie je cieľom API, môžete jednoducho odstrániť vlastnú udalosť.
Na záver - všimnite si, že tento článok sa zameriava iba na REST a webovú vrstvu - aby ste sa dostali hlbšie do časti stránkovania s prístupom k údajom, môžete si prečítať tento článok o Stránkovaní pomocou jarných údajov.
4. Viditeľnosť pre REST stránkovanie
V rámci stránkovania vyhovujúce HATEOAS obmedzenie REST znamená umožnenie klientovi API zistiť Ďalšie a predchádzajúci stránky založené na aktuálnej stránke v navigácii. Pre tento účel, použijeme Odkaz Hlavička HTTP spojená s „Ďalšie“, “prev“, “najprv“A„posledný”Typy vzťahov odkazov.
V REST Viditeľnosť je prierezovým záujmom, uplatniteľné nielen na konkrétne operácie, ale aj na typy operácií. Napríklad pri každom vytvorení zdroja by mal byť klient identifikovateľný pre URI daného zdroja. Pretože táto požiadavka je relevantná pre vytvorenie AKÉHOKOLI Zdroja, budeme ju vybavovať osobitne.
Tieto obavy oddelíme pomocou udalostí, o čom sme hovorili v predchádzajúcom článku zameranom na zistiteľnosť služby REST. V prípade stránkovania sa udalosť - PaginatedResultsRetrievedEvent - je spustený vo vrstve radiča. Potom implementujeme zistiteľnosť pre túto udalosť pomocou vlastného poslucháča.
Stručne povedané, poslucháč skontroluje, či navigácia umožňuje a Ďalšie, predchádzajúci, najprv a posledný strán. Ak áno, bude pridať príslušné URI do odpovede ako hlavička HTTP „Prepojiť“.
Poďme teraz krok za krokom. The UriComponentsBuilder odovzdané z radiča obsahuje iba základnú adresu URL (hostiteľ, port a kontextová cesta). Preto budeme musieť pridať zvyšné časti:
void addLinkHeaderOnPagedResourceRetrieval (UriComponentsBuilder uriBuilder, HttpServletResponse response, Class clazz, int page, int totalPages, int size) {String resourceName = clazz.getSimpleName (). toString (). toLowerCase (); uriBuilder.path ("/ admin /" + nazov prostriedku); // ...}
Ďalej použijeme a StringJoiner spojiť každý odkaz. Použijeme uriBuilder na generovanie URI. Pozrime sa, ako by sme pokračovali s odkazom na Ďalšie stránka:
StringJoiner linkHeader = nový StringJoiner (","); if (hasNextPage (page, totalPages)) {String uriForNextPage = constructNextPageUri (uriBuilder, page, size); linkHeader.add (createLinkHeader (uriForNextPage, "next")); }
Pozrime sa na logiku constructNextPageUri metóda:
String constructNextPageUri (UriComponentsBuilder uriBuilder, int page, int size) {return uriBuilder.replaceQueryParam (PAGE, page + 1) .replaceQueryParam ("size", size) .build () .encode () .toUriString (); }
Podobne budeme postupovať aj pri zvyšných URI, ktoré chceme zahrnúť.
Nakoniec pridáme výstup ako hlavičku odpovede:
response.addHeader ("Odkaz", linkHeader.toString ());
Všimnite si, že som tu pre stručnosť uviedol iba čiastočnú ukážku kódu a celý kód.
5. Vyskúšajte stránkovanie pri jazde
Hlavnú logiku stránkovania aj objaviteľnosti pokrývajú malé a zamerané integračné testy. Rovnako ako v predchádzajúcom článku použijeme knižnicu so zaistením REST na konzumáciu služby REST a na overenie výsledkov.
Toto je niekoľko príkladov testov integrácie stránkovania; kompletnú testovaciu sadu nájdete v projekte GitHub (odkaz na konci článku):
@Test public void whenResourcesAreRetrievedPaged_then200IsReceived () {Response response = RestAssured.get (paths.getFooURL () + "? Page = 0 & size = 2"); assertThat (response.getStatusCode (), je (200)); } @Test public void whenPageOfResourcesAreRetrievedOutOfBounds_then404IsReceived () {String url = getFooURL () + "? Page =" + randomNumeric (5) + "& size = 2"; Odozva na odpoveď = RestAssured.get.get (url); assertThat (response.getStatusCode (), je (404)); } @Test public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources () {createResource (); Odozva response = RestAssured.get (paths.getFooURL () + "? Page = 0 & size = 2"); assertFalse (response.body (). as (List.class) .isEmpty ()); }
6. Vyskúšajte rozpoznávanie jazdných stránok
Testovanie toho, či je stránkovanie klientom viditeľné, je pomerne jednoduché, aj keď je potrebné pokryť veľa priestoru.
Testy sa zamerajú na polohu aktuálnej stránky v navigácii a rôzne URI, ktoré by mali byť viditeľné z každej pozície:
@Test public void whenFirstPageOfResourcesAreRetrieved_thenSecondPageIsNext () {Response response = RestAssured.get (getFooURL () + "? Page = 0 & size = 2"); Reťazec uriToNextPage = extractURIByRel (response.getHeader ("Link"), "next"); assertEquals (getFooURL () + "? page = 1 & size = 2", uriToNextPage); } @Test public void whenFirstPageOfResourcesAreRetrieved_thenNoPreviousPage () {Response response = RestAssured.get (getFooURL () + "? Page = 0 & size = 2"); Reťazec uriToPrevPage = extractURIByRel (response.getHeader ("Link"), "prev"); assertNull (uriToPrevPage); } @Test public void whenSecondPageOfResourcesAreRetrieved_thenFirstPageIsPrevious () {Response response = RestAssured.get (getFooURL () + "? Page = 1 & size = 2"); Reťazec uriToPrevPage = extractURIByRel (response.getHeader ("Link"), "prev"); assertEquals (getFooURL () + "? page = 0 & size = 2", uriToPrevPage); } @Test public void whenLastPageOfResourcesIsRetrieved_thenNoNextPageIsDiscoverable () {Response first = RestAssured.get (getFooURL () + "? Page = 0 & size = 2"); Reťazec uriToLastPage = extractURIByRel (first.getHeader ("Odkaz"), "posledný"); Odozva na odpoveď = RestAssured.get (uriToLastPage); Reťazec uriToNextPage = extractURIByRel (response.getHeader ("Link"), "next"); assertNull (uriToNextPage); }
Upozorňujeme, že celý nízkoúrovňový kód pre extraktURIByRel - zodpovedný za extrakciu URI do rel vzťah je tu.
7. Získanie všetkých zdrojov
Na rovnakú tému stránkovania a objaviteľnosti musí byť urobená voľba, ak má klient povolené načítať všetky Zdroje v systéme naraz, alebo ak o ne musí požiadať.
Ak sa rozhodne, že klient nemôže načítať všetky zdroje pomocou jednej žiadosti a stránkovanie nie je voliteľné, ale povinné, potom je k dispozícii niekoľko možností pre odpoveď na požiadavku získať všetky. Jednou z možností je vrátiť 404 (Nenájdené) a použite Odkaz hlavička, aby bola prvá stránka viditeľná:
Odkaz =; rel = ”prvý”,; rel = ”last”
Ďalšou možnosťou je vrátiť presmerovanie - 303 (Pozri Ostatné) - na prvú stranu. Konzervatívnejšou cestou by bolo jednoducho vrátiť klientovi 405 (Metóda nie je povolená) pre požiadavku GET.
8. REST stránkovanie s Rozsah Hlavičky HTTP
Relatívne odlišným spôsobom implementácie stránkovania je práca s HTTP Rozsah hlavičky – Rozsah, Rozsah obsahu, If-Range, Prijať rozsahy - a Stavové kódy HTTP – 206 (Čiastočný obsah), 413 (Požadovaná entita je priveľká), 416 (Požadovaný rozsah nie je uspokojivý).
Jedným z názorov na tento prístup je, že rozšírenia rozsahu HTTP neboli určené na stránkovanie a že by mali byť spravované serverom, nie aplikáciou. Implementácia stránkovania na základe rozšírení hlavičiek HTTP Range je napriek tomu technicky možná, aj keď nie je tak bežná ako implementácia uvedená v tomto článku.
9. Jarné údaje REST stránkovanie
Ak v aplikácii Spring Data potrebujeme vrátiť niekoľko výsledkov z celého súboru údajov, môžeme použiť ľubovoľné Stránkovateľné metóda úložiska, pretože vždy vráti a Strana. Výsledky sa vrátia na základe čísla stránky, veľkosti stránky a smeru triedenia.
Spring Data REST automaticky rozpozná parametre adresy URL ako stránka, veľkosť, triedenie atď.
Ak chcete použiť metódy stránkovania ktoréhokoľvek úložiska, musíme si ich rozšíriť PagingAndSortingRepository:
verejné rozhranie SubjectRepository rozširuje PagingAndSortingRepository {}
Ak zavoláme // localhost: 8080 / predmety Jar automaticky pridáva stránka, veľkosť, triedenie návrhy parametrov pomocou API:
"_links": {"self": {"href": "// localhost: 8080 / Subjects {? page, size, sort}", "templated": true}}
Štandardne je veľkosť stránky 20, ale môžeme ju zmeniť tak, že zavoláme niečo ako // localhost: 8080 / Subjekty? strana = 10.
Ak chceme implementovať stránkovanie do nášho vlastného vlastného úložiska API, musíme odovzdať ďalšie Stránkovateľné parameter a uistite sa, že API vracia a Stránka:
@RestResource (cesta = "nameContains") verejná stránka findByNameContaining (@Param ("name") Názov reťazca, stránkovateľné p);
Kedykoľvek pridáme vlastné API a /Vyhľadávanie koncový bod sa pridá k vygenerovaným odkazom. Ak teda zavoláme // localhost: 8080 / Subjekty / Vyhľadávanie uvidíme koncový bod schopný stránkovania:
"findByNameContaining": {"href": "// localhost: 8080 / Subjects / search / nameContains {? name, page, size, sort}", "templated": true}
Všetky implementované API PagingAndSortingRepository vráti a Strana. Ak potrebujeme vrátiť zoznam výsledkov z Stránka, the getContent () API z Strana poskytuje zoznam záznamov načítaných v dôsledku rozhrania Spring Data REST API.
Kód v tejto časti je k dispozícii v projekte spring-data-rest.
10. Premeniť a Zoznam do a Strana
Predpokladajme, že máme a Stránkovateľné objekt ako vstup, ale informácie, ktoré potrebujeme načítať, sú obsiahnuté v zozname namiesto a PagingAndSortingRepository. V týchto prípadoch možno budeme musieť konvertovať a Zoznam do a Strana.
Predstavte si napríklad, že máme zoznam výsledkov zo služby SOAP:
Zoznam zoznam = getListOfFooFromSoapService ();
Musíme sa dostať do zoznamu na konkrétnych pozíciách určených Stránkovateľné objekt, ktorý nám bol zaslaný. Definujme teda začiatočný index:
int start = (int) pageable.getOffset ();
A konečný index:
int end = (int) ((start + pageable.getPageSize ())> fooList.size ()? fooList.size (): (start + pageable.getPageSize ()));
Keď budeme mať tieto dva na mieste, môžeme vytvoriť a Strana na získanie zoznamu prvkov medzi nimi:
Stránka page = new PageImpl (fooList.subList (start, end), pageable, fooList.size ());
To je všetko! Teraz sa môžeme vrátiť stránke ako platný výsledok.
A všimnite si, že ak chceme tiež podporiť triedenie, musíme zoraďte zoznam pred podzoznamom to.
11. Záver
Tento článok ilustroval, ako implementovať stránkovanie v rozhraní REST API pomocou pružiny, a diskutoval o tom, ako nastaviť a otestovať viditeľnosť.
Ak chcete podrobne preskúmať stránkovanie na úrovni perzistencie, pozrite si moje návody na stránkovanie JPA alebo Hibernate.
Implementáciu všetkých týchto príkladov a útržkov kódu nájdete v projekte GitHub - jedná sa o projekt založený na Maven, takže by malo byť ľahké ho importovať a spustiť tak, ako je.
REST spodok