Inleiding tot het functionele webraamwerk in het voorjaar 5

1. Inleiding

Spring WebFlux is een nieuw functioneel webraamwerk dat is gebouwd met behulp van reactieve principes.

In deze tutorial leren we hoe we er in de praktijk mee kunnen werken.

We baseren dit op onze bestaande gids voor Spring 5 WebFlux. In die handleiding hebben we een eenvoudige reactieve REST-applicatie gemaakt met behulp van op annotaties gebaseerde componenten. Hier gebruiken we in plaats daarvan het functionele raamwerk.

2. Maven Afhankelijkheid

We hebben hetzelfde nodig spring-boot-starter-webflux afhankelijkheid zoals gedefinieerd in het vorige artikel:

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

3. Functioneel webframework

Het functionele webframework introduceert een nieuw programmeermodel waarbij we functies gebruiken om verzoeken te routeren en af ​​te handelen.

In tegenstelling tot het op annotaties gebaseerde model waar we annotatietoewijzingen gebruiken, gebruiken we hier HandlerFunctie en RouterFuncties.

Evenzo is, net als bij de geannoteerde controllers, de functionele eindpuntenbenadering gebouwd op dezelfde reactieve stapel.

3.1. HandlerFunctie

De HandlerFunctie vertegenwoordigt een functie die antwoorden genereert voor verzoeken die naar hen worden gerouteerd:

@FunctionalInterface openbare interface HandlerFunction {Mono-handle (ServerRequest-verzoek); }

Deze interface is in de eerste plaats een Functie, die zich heel erg gedraagt ​​als een servlet.

Hoewel, vergeleken met een standaard Servlet # service (ServletRequest req, ServletResponse res), HandlerFunctie accepteert geen reactie als invoerparameter.

3.2. RouterFunctie

RouterFunctie dient als alternatief voor de @RequestMapping annotatie. We kunnen het gebruiken om verzoeken naar de handlerfuncties te routeren:

@FunctionalInterface openbare interface RouterFunction {Mono route (ServerRequest-verzoek); // ...}

Meestal kunnen we de helperfunctie importeren RouterFunctions.route () om routes te creëren, in plaats van een volledige routerfunctie te schrijven.

Het stelt ons in staat om verzoeken te routeren door een Verzoek Predicaat. Als het predikaat overeenkomt, wordt het tweede argument, de handlerfunctie, geretourneerd:

openbare statische RouterFunction-route (RequestPredicate-predikaat, HandlerFunction-handlerFunction)

Omdat de route() methode retourneert een RouterFunctie, kunnen we het koppelen om krachtige en complexe routingschema's te bouwen.

4. Reactieve REST-applicatie met behulp van functioneel web

In onze vorige gids hebben we een eenvoudig Werknemersbeheer REST-applicatie met @RestController en Web cliënt.

Laten we nu dezelfde logica implementeren met behulp van router- en handlerfuncties.

Eerste, we moeten routes maken met RouterFunctie om onze reactieve stromen van Werknemers.

Routes worden geregistreerd als Spring Beans en kunnen binnen elke configuratieklasse worden aangemaakt.

4.1. Eén bron

Laten we onze eerste route maken met RouterFunctie die een single publiceert Werknemer bron:

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

Het eerste argument is een verzoekpredikaat. Merk op hoe we een statisch geïmporteerd RequestPredicates.GET methode hier. De tweede parameter definieert een handlerfunctie die zal worden gebruikt als het predikaat van toepassing is.

Met andere woorden, het bovenstaande voorbeeld routeert alle GET-verzoeken voor / medewerkers / {id} naar EmployeeRepository # findEmployeeById (tekenreeks-id) methode.

4.2. Collectiebron

Laten we vervolgens voor het publiceren van een verzamelingsresource een andere route toevoegen:

@Bean RouterFunction getAllEmployeesRoute () {retourroute (GET ("/ workers"), req -> ok (). Body (employeeRepository (). FindAllEmployees (), Employee.class)); }

4.3. Update voor één bron

Laten we tot slot een route toevoegen om het Werknemer bron:

@Bean RouterFunction updateEmployeeRoute () {retour route (POST ("/ workers / update"), req -> req.body (toMono (Employee.class)) .doOnNext (employeeRepository () :: updateEmployee) .then (ok () .bouwen())); }

5. Routes samenstellen

Ook kunnen we de routes samen in één routerfunctie samenstellen.

Laten we eens kijken hoe we de hierboven gemaakte routes kunnen combineren:

@Bean RouterFunction samengesteldRoutes () {retour route (GET ("/ workers"), req -> ok (). Body (employeeRepository (). FindAllEmployees (), Employee.class)). En (route (GET ("/ workers / {id} "), req -> ok (). body (employeeRepository (). findEmployeeById (req.pathVariable (" id ")), Employee.class))). en (route (POST (" / workers / update "), req -> req.body (toMono (Employee.class)) .doOnNext (employeeRepository () :: updateEmployee) .then (ok (). build ()))); }

Hier hebben we gebruikt RouterFunction.and () om onze routes te combineren.

Ten slotte hebben we de volledige REST API geïmplementeerd die nodig is voor onze Werknemersbeheer applicatie, met behulp van routers en handlers.

Om de applicatie uit te voeren, kunnen we afzonderlijke routes gebruiken of de enkele, samengestelde route die we hierboven hebben gemaakt.

6. Routes testen

We kunnen gebruiken WebTestClient om onze routes te testen.

Om dit te doen, moeten we eerst de routes binden met behulp van de bindToRouterFunction methode en bouw vervolgens de testclientinstantie.

Laten we onze getEmployeeByIdRoute:

@Test openbare ongeldig gegevenEmployeeId_whenGetEmployeeById_thenCorrectEmployee () {WebTestClient-client = WebTestClient .bindToRouterFunction (config.getEmployeeByIdRoute ()) .build (); Werknemer werknemer = nieuwe werknemer ("1", "Werknemer 1"); gegeven (employeeRepository.findEmployeeById ("1")). willReturn (Mono.just (werknemer)); client.get () .uri ("/ werknemers / 1") .exchange () .expectStatus () .isOk () .expectBody (Employee.class) .isEqualTo (medewerker); }

en soortgelijk getAllEmployeesRoute:

@Test openbare ongeldigheid whenGetAllEmployees_thenCorrectEmployees () {WebTestClient-client = WebTestClient .bindToRouterFunction (config.getAllEmployeesRoute ()) .build (); Lijst werknemers = Arrays.asList (nieuwe werknemer ("1", "werknemer 1"), nieuwe werknemer ("2", "werknemer 2")); Flux medewerkerFlux = Flux.fromIterable (medewerkers); gegeven (employeeRepository.findAllEmployees ()). willReturn (employeeFlux); client.get () .uri ("/ werknemers") .exchange () .expectStatus () .isOk () .expectBodyList (Employee.class) .isEqualTo (werknemers); }

We kunnen ook onze updateEmployeeRoute door te beweren dat onze Werknemer instantie wordt bijgewerkt via WerknemerRepository:

@Test openbare ongeldigheid whenUpdateEmployee_thenEmployeeUpdated () {WebTestClient-client = WebTestClient .bindToRouterFunction (config.updateEmployeeRoute ()) .build (); Werknemer werknemer = nieuwe werknemer ("1", "Werknemer 1 bijgewerkt"); client.post () .uri ("/ werknemers / update") .body (Mono.just (werknemer), Employee.class) .exchange () .expectStatus () .isOk (); verifieer (employeeRepository) .updateEmployee (medewerker); }

Voor meer informatie over testen met WebTestClient raadpleeg onze tutorial over werken met Web cliënt en WebTestClient.

7. Samenvatting

In deze tutorial hebben we het nieuwe functionele webframework in Spring 5 geïntroduceerd en de twee kerninterfaces bekeken: RouterFunctie en HandlerFunctie. We hebben ook geleerd hoe we verschillende routes kunnen maken om het verzoek af te handelen en het antwoord te verzenden.

Bovendien hebben we onze Werknemersbeheer toepassing geïntroduceerd in gids voor Spring 5 WebFlux met het functionele eindpuntenmodel.

Zoals altijd is de volledige broncode te vinden op Github.