Paginering med Spring REST og AngularJS bord

REST Topp

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

1. Oversikt

I denne artikkelen vil vi hovedsakelig fokusere på å implementere paginering av serversiden i en Våren REST API og en enkel AngularJS frontend.

Vi vil også utforske et ofte brukt bordgitter i Angular med navnet UI Grid.

2. Avhengigheter

Her beskriver vi forskjellige avhengigheter som kreves for denne artikkelen.

2.1. JavaScript

For at Angular UI Grid skal fungere, trenger vi skriptene nedenfor importert i HTML-en.

  • Vinkel JS (1.5.8)
  • Vinkel UI-rutenett

2.2. Maven

For vår backend vil vi bruke Vårstøvel, så vi trenger avhengighetene nedenfor:

 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat provided 

Merk: Andre avhengigheter ble ikke spesifisert her, for å se hele listen, se hele listen pom.xml i GitHub-prosjektet.

3. Om applikasjonen

Applikasjonen er en enkel elevkatalog-app som lar brukerne se studentdetaljene i et paginert tabellrutenett.

Søknaden bruker Vårstøvel og kjører i en innebygd Tomcat-server med en innebygd database.

Til slutt, på API-siden av ting, er det noen måter å gjøre paginering på, beskrevet i REST Pagination in Spring-artikkelen her - som anbefales å lese i forbindelse med denne artikkelen.

Vår løsning her er enkel - å ha informasjon om personsøk i et URI-spørsmål som følger: / student / get? page = 1 & size = 2.

4. Kundesiden

Først må vi lage klientside-logikken.

4.1. UI-Grid

Våre index.html vil ha importen vi trenger og en enkel implementering av tabellrutenettet:

La oss se nærmere på koden:

  • ng-app - er Angular-direktivet som laster inn modulen app. Alle elementene under disse vil være en del av app modul
  • ng-kontroller - er Angular-direktivet som laster kontrolleren StudentCtrl med et alias for vm. Alle elementene under disse vil være en del av StudentCtrl kontrolleren
  • ui-grid - er Angular-direktivet som tilhører Angular ui-grid og bruker gridOptions som standardinnstillinger, gridOptions er erklært under $ omfang i app.js

4.2. AngularJS-modulen

La oss først definere modulen i app.js:

var app = angular.module ('app', ['ui.grid', 'ui.grid.pagination']);

Vi erklærte app modul og vi injiserte ui.grid for å aktivere UI-Grid-funksjonalitet; vi injiserte også ui.grid.pagination for å aktivere sidestøtting.

Deretter definerer vi kontrolleren:

app.controller ('StudentCtrl', ['$ scope', 'StudentService', funksjon ($ scope, StudentService) {var paginationOptions = {pageNumber: 1, pageSize: 5, sort: null}; StudentService.getStudents (paginationOptions.pageNumber , paginationOptions.pageSize) .success (funksjon (data) {$ scope.gridOptions.data = data.content; $ scope.gridOptions.totalItems = data.totalElements;}); $ scope.gridOptions = {paginationPageSizes: [5, 10 , 20], paginationPageSize: paginationOptions.pageSize, enableColumnMenus: false, useExternalPagination: true, columnDefs: [{name: 'id'}, {name: 'name'}, {name: 'gender'}, {name: 'age '}], onRegisterApi: function (gridApi) {$ scope.gridApi = gridApi; gridApi.pagination.on.paginationChanged ($ scope, function (newPage, pageSize) {paginationOptions.pageNumber = newPage; paginationOptions.pageSize = pageSize; StudentService. getStudents (newPage, pageSize) .success (funksjon (data) {$ scope.gridOptions.data = data.content; $ scope.gridOptions.totalItems = data.totalElements;}); }); }}; }]); 

La oss nå ta en titt på de tilpassede innstillingene for paginering i $ scope.gridOptions:

  • paginationPageSizes - definerer tilgjengelige sidestørrelsesalternativer
  • paginationPageSize - definerer standard sidestørrelse
  • enableColumnMenus - brukes til å aktivere / deaktivere menyen i kolonnene
  • useExternalPagination - kreves hvis du paginerer på serversiden
  • columnDefs - kolonnenavnene som automatisk blir kartlagt til JSON-objektet som returneres fra serveren. Feltnavnene i JSON-objektet som returneres fra serveren og kolonnenavnet som er definert, skal stemme overens.
  • onRegisterApi - muligheten til å registrere offentlige metodebegivenheter i nettet. Her registrerte vi gridApi.pagination.on.paginationChanged å fortelle UI-Grid å utløse denne funksjonen når siden ble endret.

Og for å sende forespørselen til API:

app.service ('StudentService', ['$ http', function ($ http) {function getStudents (pageNumber, size) {pageNumber = pageNumber> 0? pageNumber - 1: 0; returner $ http ({metode: 'GET' , url: 'student / get? page =' + pageNumber + '& size =' + size});} returner {getStudents: getStudents};}]);

5. Backend og API

5.1. Den RESTful Service

Her er den enkle RESTful API-implementeringen med paginasjonsstøtte:

@RestController offentlig klasse StudentDirectoryRestController {@Autowired privat StudentService-tjeneste; @RequestMapping (verdi = "/ student / get", params = {"side", "størrelse"}, metode = RequestMethod.GET) offentlig side findPaginated (@RequestParam ("side") int side, @RequestParam ("størrelse") ) int størrelse) {Side resultPage = service.findPaginated (side, størrelse); hvis (side> resultPage.getTotalPages ()) {kast ny MyResourceNotFoundException (); } return resultPage; }}

De @RestController ble introdusert våren 4.0 som en bekvemmelighetsnotering som implisitt erklærer @Kontrollør og @ResponseBody.

For API-en vår erklærte vi at den godtar to parametere som er side og størrelse som også vil bestemme antall poster som skal returneres til klienten.

Vi la også til en enkel validering som vil kaste a MyResourceNotFoundException hvis sidetallet er høyere enn det totale antallet sider.

Til slutt kommer vi tilbake Side som svar - dette er en super nyttig komponent i Spring-data som har holdt paginasjonsdata.

5.2. Tjenesteimplementeringen

Tjenesten vår vil ganske enkelt returnere postene basert på side og størrelse gitt av kontrolleren:

@Service offentlig klasse StudentServiceImpl implementerer StudentService {@Autowired privat StudentRepository dao; @ Overstyr offentlig side findPaginated (int-side, int-størrelse) {return dao.findAll (ny PageRequest (side, størrelse)); }} 

5.3. Repository Implementation

For vårt utholdenhetslag bruker vi en innebygd database og Spring Data JPA.

Først må vi konfigurere vår utholdenhetskonfigurasjon:

@EnableJpaRepositories ("com.baeldung.web.dao") @ComponentScan (basePackages = {"com.baeldung.web"}) @EntityScan ("com.baeldung.web.entity") @Configuration public class PersistenceConfig {@Bean public JdbcTemplate getJdbcTemplate () {return new JdbcTemplate (dataSource ()); } @Bean public DataSource dataSource () {EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder (); EmbeddedDatabase db = builder .setType (EmbeddedDatabaseType.HSQL) .addScript ("db / sql / data.sql") .build (); returnere db; }} 

Persistenskonfigurasjonen er enkel - det har vi @EnableJpaRepositories for å skanne den spesifiserte pakken og finne våre Spring Data JPA repository grensesnitt.

Vi har @ComponentScan her for å automatisk skanne etter alle bønner og vi har @EntityScan (fra Spring Boot) for å skanne etter enhetsklasser.

Vi erklærte også vår enkle datakilde - ved hjelp av en innebygd database som kjører SQL-skriptet som ble gitt ved oppstart.

Nå er det på tide at vi lager datalageret vårt:

offentlig grensesnitt StudentRepository utvider JpaRepository {} 

Dette er i utgangspunktet alt vi trenger å gjøre her; Hvis du vil gå dypere inn i hvordan du konfigurerer og bruker den kraftige Spring Data JPA, kan du definitivt lese veiledningen til den her.

6. Forespørsel om svar og svar

Når du ringer til API - // localhost: 8080 / student / get? Page = 1 & size = 5vil JSON-svaret se ut slik:

{"content": [{"studentId": "1", "name": "Bryan", "gender": "Male", "age": 20}, {"studentId": "2", "name" : "Ben", "gender": "Male", "age": 22}, {"studentId": "3", "name": "Lisa", "gender": "Female", "age": 24 }, {"studentId": "4", "name": "Sarah", "gender": "Female", "age": 26}, {"studentId": "5", "name": "Jay" , "gender": "Male", "age": 20}], "last": false, "totalElements": 20, "totalPages": 4, "size": 5, "number": 0, "sort" : null, "first": true, "numberOfElements": 5} 

En ting å legge merke til her er at serveren returnerer en org.springframework.data.domain.Page DTO, innpakning av vår Student Ressurser.

De Side objektet vil ha følgende felt:

  • siste - satt til ekte hvis det er den siste siden ellers falsk
  • først - satt til ekte hvis det er den første siden ellers falsk
  • totalElements - totalt antall rader / poster. I vårt eksempel ga vi dette til ui-grid alternativer $ scope.gridOptions.totalItems for å bestemme hvor mange sider som vil være tilgjengelige
  • totalSider - det totale antallet sider som ble hentet fra (totalElements / størrelse)
  • størrelse - antall poster per side, dette ble sendt fra klienten via param størrelse
  • Nummer - sidenummeret som sendes av klienten, som svar er tallet 0 fordi vi bruker en rekke av i vår backend Students som er en nullbasert indeks, så i vår backend reduserer vi sidetallet med 1
  • sortere - sorteringsparameteren for siden
  • numberOfElements - antall rader / poster som returneres for siden

7. Testing av paginering

La oss nå sette opp en test for vår paginasjonslogikk, ved hjelp av RestAssured; for å lære mer om RestAssured du kan se på denne veiledningen.

7.1. Forbereder testen

For å lette utviklingen av testklassen vil vi legge til den statiske importen:

io.restassured.RestAssured. * io.restassured.matcher.RestAssuredMatchers. * org.hamcrest.Matchers. *

Deretter setter vi opp våren aktivert test:

@RunWith (SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration (klasser = Application.class) @WebAppConfiguration @IntegrationTest ("server.port: 8888") 

De @SpringApplicationConfiguration hjelper våren å vite hvordan du laster Søknadskontekst, i dette tilfellet brukte vi Application.java for å konfigurere vår ApplicationContext.

De @WebAppConfiguration ble definert for å fortelle våren at ApplicationContext som skal lastes skal være en WebApplicationContext.

Og @IntegrasjonTest ble definert for å utløse oppstart av applikasjonen når testen kjøres, dette gjør REST-tjenestene våre tilgjengelige for testing.

7.2. Testene

Her er vår første testsak:

@Test offentlig ugyldig givenRequestForStudents_whenPageIsOne_expectContainsNames () {gitt (). Params ("page", "0", "size", "2"). Get (ENDPOINT) .then () .assertThat (). Body ("content.name ", hasItems (" Bryan "," Ben ")); } 

Denne testsaken ovenfor er for å teste at når side 1 og størrelse 2 sendes til REST-tjenesten, bør JSON-innholdet som returneres fra serveren ha navnene Bryan og Ben.

La oss dissekere testsaken:

  • gitt - den delen av RestAssured og brukes til å starte forespørselen, kan du også bruke med()
  • - den delen av RestAssured og hvis brukt utløser en get-forespørsel, bruk post () for post-forespørsel
  • hasItems - den delen av hamcrest som sjekker om verdiene stemmer overens

Vi legger til noen flere testtilfeller:

@Test offentlig ugyldig givenRequestForStudents_whenResourcesAreRetrievedPaged_thenExpect200 () {given (). Params ("page", "0", "size", "2"). Get (ENDPOINT) .then () .statusCode (200); }

Denne testen hevder at når poenget faktisk kalles, mottas et OK-svar:

@Test offentlig ugyldig givenRequestForStudents_whenSizeIsTwo_expectNumberOfElementsTwo () {given (). Params ("page", "0", "size", "2"). Get (ENDPOINT) .then () .assertThat (). Body ("numberOfElements", equalTo (2)); }

Denne testen hevder at når sidestørrelse på to blir bedt om, er sidestørrelsen som returneres faktisk to:

@Test offentlig ugyldig givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources () {given (). Params ("page", "0", "size", "2"). Get (ENDPOINT) .then () .assertThat (). Body ("first", equalTo (true)); } 

Denne testen hevder at når ressursene blir kalt første gang, er verdien for første side navn sann.

Det er mange flere tester i depotet, så ta en titt på GitHub-prosjektet.

8. Konklusjon

Denne artikkelen illustrerte hvordan du implementerer et datatabellnett ved hjelp av UI-Grid i AngularJS og hvordan du implementerer den nødvendige paginering på serversiden.

Implementeringen av disse eksemplene og testene finner du i GitHub-prosjektet. Dette er et Maven-prosjekt, så det skal være enkelt å importere og kjøre som det er.

For å kjøre vårstartprosjektet kan du ganske enkelt gjøre det mvn spring-boot: run og få tilgang til den lokalt på // lokal vert: 8080 /.

HVILLE bunnen

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

$config[zx-auto] not found$config[zx-overlay] not found