header

Spring Webflux el lado del Servidor

Este post técnico nos llevará a través del proceso de crear end-points usando Spring Webflux. Por favor lee mi previo post Spring Webflux Basics antes de continuar con esta información. Vamos a usar @RestController para poder servir nuestros datos desde PersonRepository. Consideremos el siguiente ejemplo:

package com.jos.dem.webflux.controller;

import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;

import com.jos.dem.webflux.model.Person;
import com.jos.dem.webflux.repository.PersonRepository;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RestController
public class PersonController {

  private Logger log = LoggerFactory.getLogger(this.getClass());

  @Autowired
  private PersonRepository personRepository;

  @GetMapping("/persons")
  public Flux<Person> findAll(){
    log.info("Calling find persons");
    return personRepository.findAll();
  }

  @GetMapping("/persons/{nickname}")
  public Mono<Person> findById(@PathVariable String nickname){
    log.info("Calling find person by id: {}", nickname);
    return personRepository.findById(nickname);
  }

}

Spring Web MVC fue construido pensando en Servlet API y Servlet containers y el servidor de aplicaciones comúnmente usado era Tomcat. El framework reactivo Spring Webflux es pensado en ser no-bloqueante, soporado por reactive strams y que corra unsando Netty server. Cuando usamos @RestController indicamos que no queremos renderear vistas, pero escribir los resultados directo al cuerpo de la respuesta. @GetMapping es la forma corta de @RequestMapping(method=RequestMethod.GET). Así, después de agregar este controller al proyecto y correr Spring Boot, tú puedes ir a: http://localhost:8080/persons

[
   {
      "nickname":"mkheck",
      "email":"mkheck@email.com"
   },
   {
      "nickname":"josdem",
      "email":"joseluis.delacruz@gmail.com"
   },
   {
      "nickname":"edzero",
      "email":"edzero@email.com"
   },
   {
      "nickname":"siedrix",
      "email":"siedrix@email.com"
   },
   {
      "nickname":"tgrip",
      "email":"tgrip@email.com"
   }
}

Este es el endpoint para obtener una persona por id: http://localhost:8080/persons/josdem

{
  "nickname": "josdem",
  "email": "josdem@email.com"
}

Testeando la Capa Web

La mejor herramienta para testear este reactivo servidor web es WebTestClient el cual es también un cliente no-bloqueante, reactivo y usa WebClient internamente para hacer peticiones y validar respuestas.

package com.jos.dem.webflux;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

import com.jos.dem.webflux.model.Person;
import com.jos.dem.webflux.controller.PersonController;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class PersonControllerTest {

  @Autowired
  private WebTestClient webClient;

  @Test
  public void shouldGetPersons() throws Exception {
    webClient.get().uri("/persons").accept(APPLICATION_JSON)
      .exchange()
      .expectStatus().isOk()
      .expectHeader().contentType(APPLICATION_JSON_UTF8)
      .expectBodyList(Person.class);
  }

  @Test
  public void shouldGetPerson() throws Exception {
    webClient.get().uri("/persons/{nickname}", "josdem").accept(APPLICATION_JSON)
      .exchange()
      .expectStatus().isOk()
      .expectHeader().contentType(APPLICATION_JSON_UTF8)
      .expectBody(Person.class);
  }

}

No olviden agregar Junit Jupiter conocido también como Junit5 a tu archivo build.gradle

testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion"
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion"

O a tu archivo pom.xml

<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-api</artifactId>
  <version>${junit.jupiter.version}</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-engine</artifactId>
  <version>${junit.jupiter.version}</version>
  <scope>test</scope>
</dependency>

Conclusión

Concluiremos con lo siguiente:

  • Spring Boot ahora fomenta la programación reactiva
  • Usando el estilo MVC para los controladores es una buena idea para empezar a adoptar web reactivo
  • Webflux combina bien con Java y la programación funcional
  • Webflux corre sobre Netty server
  • La mejor herramienta para testear un servidor web reactivo es WebTestClient

Para correr este proyecto con Gradle:

gradle bootRun

Para correr este proyecto con Maven:

mvn spring-boot:run

Para explorar el proyecto, por favor ve aquí, para descargar el proyecto:

git clone https://github.com/josdem/reactive-webflux-workshop.git
cd server

Regresar al artículo principal)

comments powered by Disqus