Introduktion til den funktionelle webramme i foråret 5

1. Introduktion

Spring WebFlux er en ny funktionel webramme bygget med reaktive principper.

I denne vejledning lærer vi at arbejde med det i praksis.

Vi baserer dette på vores eksisterende guide til Spring 5 WebFlux. I denne vejledning oprettede vi en simpel reaktiv REST-applikation ved hjælp af annoteringsbaserede komponenter. Her bruger vi i stedet den funktionelle ramme.

2. Maven-afhængighed

Vi har brug for det samme spring-boot-starter-webflux afhængighed som defineret i forrige artikel:

 org.springframework.boot spring-boot-starter-webflux 2.2.6.RELEASE 

3. Funktionel Web Framework

Den funktionelle webramme introducerer en ny programmeringsmodel, hvor vi bruger funktioner til at rute og håndtere anmodninger.

I modsætning til den annoteringsbaserede model, hvor vi bruger tilknytninger til annoteringer, her skal vi bruge HandlerFunktion og RouterFunktions.

På samme måde, som i de kommenterede controllere, er den funktionelle slutpunkttilgang bygget på den samme reaktive stak.

3.1. HandlerFunktion

Det HandlerFunktion repræsenterer en funktion, der genererer svar på anmodninger sendt til dem:

@FunctionalInterface offentlig grænseflade HandlerFunction {Monohåndtag (ServerRequest-anmodning); }

Denne grænseflade er primært en Fungere, der opfører sig meget som en servlet.

Selvom sammenlignet med en standard Servlet # service (ServletRequest req, ServletResponse res), HandlerFunktion tager ikke et svar som inputparameter.

3.2. RouterFunktion

RouterFunktion fungerer som et alternativ til @RequestMapping kommentar. Vi kan bruge den til at dirigere anmodninger til handlerfunktionerne:

@FunctionalInterface offentlig grænseflade RouterFunction {Mono rute (ServerRequest anmodning); // ...}

Vi kan typisk importere hjælpefunktionen RouterFunctions.route () for at oprette ruter i stedet for at skrive en komplet routerfunktion.

Det giver os mulighed for at dirigere anmodninger ved at anvende en RequestPredicate. Når prædikatet matches, returneres det andet argument, handlerfunktionen:

public static RouterFunction route (RequestPredicate predicate, HandlerFunction handlerFunction)

Fordi rute() metode returnerer a RouterFunktion, kan vi kæde det for at opbygge kraftfulde og komplekse routing-ordninger.

4. Reaktiv REST-applikation ved hjælp af funktionelt web

I vores tidligere guide oprettede vi en simpel Medarbejderledelse REST-applikation ved hjælp af @RestController og Webklient.

Lad os nu implementere den samme logik ved hjælp af router- og handlerfunktioner.

Først, vi er nødt til at oprette ruter ved hjælp af RouterFunktion at offentliggøre og forbruge vores reaktive strømme af Medarbejders.

Ruter er registreret som springbønner og kan oprettes inden for enhver konfigurationsklasse.

4.1. Enkelt ressource

Lad os oprette vores første rute ved hjælp af RouterFunktion der udgiver en enkelt Medarbejder ressource:

@Bean RouterFunction getEmployeeByIdRoute () {returrute (GET ("/ workers / {id}"), req -> ok (). Body (medarbejderRepository (). FindEmployeeById (req.pathVariable ("id")), Employee.class )); }

Det første argument er et anmodningspredikat. Læg mærke til, hvordan vi brugte en statisk importeret RequestPredicates.GET metode her. Den anden parameter definerer en behandlingsfunktion, der skal bruges, hvis prædikatet gælder.

Med andre ord ruter ovenstående eksempel alle GET-anmodninger om / medarbejdere / {id} til EmployeeRepository # findEmployeeById (streng-id) metode.

4.2. Samlingsressource

Lad os derefter tilføje en anden rute til offentliggørelse af en samlingsressource:

@Bean RouterFunction getAllEmployeesRoute () {returrute (GET ("/ medarbejdere"), req -> ok (). Organ (medarbejderRepository (). FindAllEmployees (), Medarbejder.klasse)); }

4.3. En enkelt ressourceopdatering

Endelig lad os tilføje en rute til opdatering af Medarbejder ressource:

@Bean RouterFunction updateEmployeeRoute () {returrute (POST ("/ medarbejdere / opdatering"), req -> req.body (toMono (Employee.class)). DoOnNext (medarbejderRepository () :: updateEmployee). Derefter (ok () .build ())); }

5. Komponeringsruter

Vi kan også komponere ruterne sammen i en enkelt routerfunktion.

Lad os se, hvordan man kombinerer ruterne oprettet ovenfor:

@Bean RouterFunction CompoundRoutes () {returrute (GET ("/ medarbejdere"), req -> ok (). Body (medarbejderRepository (). FindAllEmployees (), Medarbejder.klasse)). Og (rute (GET ("/ medarbejdere) / {id} "), req -> ok (). body (employeeRepository (). findEmployeeById (req.pathVariable (" id ")), Employee.class)).. (route (POST (" / medarbejder / opdatering) "), req -> req.body (toMono (Medarbejder.klasse)). doOnNext (medarbejderRepository () :: updateEmployee). derefter (ok (). build ()))); }

Her har vi brugt RouterFunction.and () for at kombinere vores ruter.

Endelig har vi implementeret den komplette REST API, der er nødvendig til vores Medarbejderledelse applikation ved hjælp af routere og håndterere.

For at køre applikationen kan vi enten bruge separate ruter eller den enkelt sammensatte, som vi oprettede ovenfor.

6. Test af ruter

Vi kan bruge WebTestClient for at teste vores ruter.

For at gøre det skal vi først binde ruterne ved hjælp af bindToRouterFunction metode og derefter oprette testklientinstansen.

Lad os teste vores getEmployeeByIdRoute:

@Test offentligt ugyldigt givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee () {WebTestClient client = WebTestClient .bindToRouterFunction (config.getEmployeeByIdRoute ()) .build (); Medarbejdermedarbejder = ny medarbejder ("1", "Medarbejder 1"); given (employeeRepository.findEmployeeById ("1")). willReturn (Mono.just (medarbejder)); client.get () .uri ("/ workers / 1") .exchange () .expectStatus () .isOk () .expectBody (Employee.class) .isEqualTo (medarbejder); }

og lignende getAllEmployeesRoute:

@Test offentlig ugyldig nårGetAllEmployees_thenCorrectEmployees () {WebTestClient client = WebTestClient .bindToRouterFunction (config.getAllEmployeesRoute ()) .build (); Liste medarbejdere = Arrays.asList (ny medarbejder ("1", "Medarbejder 1"), ny medarbejder ("2", "Medarbejder 2")); Flux medarbejderFlux = Flux.fromIterable (medarbejdere); given (employeeRepository.findAllEmployees ()). willReturn (medarbejderflux); client.get () .uri ("/ workers") .exchange () .expectStatus () .isOk () .expectBodyList (Medarbejder.klasse) .isEqualTo (medarbejdere); }

Vi kan også teste vores updateEmployeeRoute ved at hævde, at vores Medarbejder forekomst opdateres via Medarbejderopbevaring:

@Test offentlig ugyldig nårUpdateEmployee_thenEmployeeUpdated () {WebTestClient-klient = WebTestClient .bindToRouterFunction (config.updateEmployeeRoute ()) .build (); Medarbejdermedarbejder = ny medarbejder ("1", "Medarbejder 1 opdateret"); client.post () .uri ("/ medarbejdere / opdatering") .body (Mono.just (medarbejder), Medarbejder.klasse) .exchange () .expectStatus () .isOk (); verificere (medarbejderRepository) .updateEmployee (medarbejder); }

For flere detaljer om test med WebTestClient se vores vejledning om at arbejde med Webklient og WebTestClient.

7. Resume

I denne vejledning introducerede vi den nye funktionelle webramme i Spring 5 og kiggede på dens to kernegrænseflader - RouterFunktion og HandlerFunktion. Vi lærte også, hvordan man opretter forskellige ruter til at håndtere anmodningen og sende svaret.

Derudover genskabte vi vores Medarbejderledelse applikation introduceret i guide til Spring 5 WebFlux med den funktionelle slutpointsmodel.

Som altid kan den fulde kildekode findes på Github.


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