printemps 5 WebClient

Printemps 5 WebClient

1. Vue d'ensemble

Dans cet article, nous allons montrer leWebClient - un client Web réactif qui sera introduit au printemps 5.

Nous allons également examiner lesWebTestClient - qui sont desWebClient conçus pour être utilisés dans les tests.

Lectures complémentaires:

Filtres WebClient Spring

En savoir plus sur les filtres WebClient dans Spring WebFlux

Read more

Requêtes WebClient Spring avec paramètres

Découvrez comment utiliser de manière réactive les points de terminaison des API REST avec WebClient à partir de Spring Webflux.

Read more

2. Que sont lesWebClient?

En termes simples,WebClient est une interface représentant le principal point d'entrée pour effectuer des requêtes Web.

Il a été créé dans le cadre du module Spring Web Reactive et remplacera lesRestTemplateclassiques dans ces scénarios. Le nouveau client est une solution réactive, non bloquante, fonctionnant sur le protocole HTTP / 1.1.

Enfin, l'interface a une seule implémentation - la classeDefaultWebClient - avec laquelle nous allons travailler.

3. Les dépendances

Puisque nous utilisons une application Spring Boot, nous avons besoin de la dépendancespring-boot-starter-webflux, ainsi que dethe Reactor project.

3.1. Construire avec Maven

Ajoutons les dépendances suivantes au fichierpom.xml:


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


    org.projectreactor
    reactor-spring
    1.0.1.RELEASE

3.2. Construire avec Gradle

Avec Gradle, nous devons ajouter les entrées suivantes au fichierbuild.gradle:

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-webflux'
    compile 'org.projectreactor:reactor-spring:1.0.1.RELEASE'
}

4. Travailler avec lesWebClient

Pour travailler correctement avec le client, nous devons savoir:

  • créer une instance

  • faire une demande

  • gérer la réponse

4.1. Création d'une instanceWebClient

Vous avez le choix entre trois options. La première consiste à créer un objetWebClient avec les paramètres par défaut:

WebClient client1 = WebClient.create();

La deuxième alternative permet de lancer une instanceWebClient avec un URI de base donné:

WebClient client2 = WebClient.create("http://localhost:8080");

La dernière méthode (et la plus avancée) consiste à créer un client en utilisant la classeDefaultWebClientBuilder, qui permet une personnalisation complète:

WebClient client3 = WebClient
  .builder()
    .baseUrl("http://localhost:8080")
    .defaultCookie("cookieKey", "cookieValue")
    .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
    .defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080"))
  .build();

4.2. Préparer une demande

Tout d'abord, nous devons spécifier une méthode HTTP d'une requête en invoquant lemethod(HttpMethod method) ou en appelant ses méthodes de raccourci telles queget,post,delete:

WebClient.UriSpec request1 = client3.method(HttpMethod.POST);
WebClient.UriSpec request2 = client3.post();

Le prochain mouvement consiste à fournir une URL. Nous pouvons le transmettre à l'APIuri - en tant qu'instanceString oujava.net.URL:

WebClient.RequestBodySpec uri1 = client3
  .method(HttpMethod.POST)
  .uri("/resource");

WebClient.RequestBodySpec uri2 = client3
  .post()
  .uri(URI.create("/resource"));

Ensuite, nous pouvons définir un corps de requête, un type de contenu, une longueur, des cookies ou des en-têtes, si nécessaire.

Par exemple, si nous voulons définir un corps de requête - il y a deux façons disponibles - de le remplir avec unBodyInserter ou de déléguer ce travail à unPublisher:

WebClient.RequestHeadersSpec requestSpec1 = WebClient
  .create()
  .method(HttpMethod.POST)
  .uri("/resource")
  .body(BodyInserters.fromPublisher(Mono.just("data")), String.class);

WebClient.RequestHeadersSpec requestSpec2 = WebClient
  .create("http://localhost:8080")
  .post()
  .uri(URI.create("/resource"))
  .body(BodyInserters.fromObject("data"));

The BodyInserter is an interface responsible for populating a ReactiveHttpOutputMessage body with a given output message and a context used during the insertion. UnPublisher est un composant réactif chargé de fournir un nombre potentiellement illimité d'éléments séquencés.

La deuxième méthode est la méthodebody, qui est un raccourci pour la méthodebody(BodyInserter inserter) d'origine.

Pour alléger ce processus de remplissage d'unBodyInserter,, il existe une classeBodyInserters avec un certain nombre de méthodes utilitaires utiles:

BodyInserter, ReactiveHttpOutputMessage> inserter1 = BodyInserters
  .fromPublisher(Subscriber::onComplete, String.class);

C'est également possible avec unMultiValueMap:

LinkedMultiValueMap map = new LinkedMultiValueMap();

map.add("key1", "value1");
map.add("key2", "value2");

BodyInserter inserter2
 = BodyInserters.fromMultipartData(map);

Ou en utilisant un seul objet:

BodyInserter inserter3
 = BodyInserters.fromObject(new Object());

Une fois le corps défini, nous pouvons définir des en-têtes, des cookies et des types de supports acceptables. Values will be added to those have been set when instantiating the client.

En outre, il existe un support supplémentaire pour les en-têtes les plus couramment utilisés tels que“If-None-Match”, “If-Modified-Since”, “Accept”, “Accept-Charset”.

Voici un exemple d'utilisation de ces valeurs:

WebClient.ResponseSpec response1 = uri1
  .body(inserter3)
    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
    .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
    .acceptCharset(Charset.forName("UTF-8"))
    .ifNoneMatch("*")
    .ifModifiedSince(ZonedDateTime.now())
  .retrieve();

4.3. Obtenir une réponse

La dernière étape consiste à envoyer la demande et à recevoir une réponse. Cela peut être fait avec les méthodesexchange ouretrieve.

Ils diffèrent par les types de retour; la méthodeexchange fournit unClientResponse avec son état, des en-têtes tandis que la méthoderetrieve est le chemin le plus court pour récupérer directement un corps:

String response2 = request1.exchange()
  .block()
  .bodyToMono(String.class)
  .block();
String response3 = request2
  .retrieve()
  .bodyToMono(String.class)
  .block();

Faites attention à la méthodebodyToMono, qui lancera unWebClientException si le code d'état est4xx (erreur client) ou5xx (erreur serveur). Nous avons utilisé la méthodeblock surMonos pour nous abonner et récupérer une donnée réelle qui a été envoyée avec la réponse.

5. Travailler avec lesWebTestClient

LeWebTestClient est le principal point d'entrée pour tester les points de terminaison du serveur WebFlux. Il a une API très similaire auxWebClient, et il délègue la plupart du travail à une instance interne deWebClient se concentrant principalement sur la fourniture d'un contexte de test. La classeDefaultWebTestClient est une implémentation d'interface unique.

Le client à tester peut être lié à un serveur réel ou utiliser des contrôleurs ou des fonctions spécifiques. Pour effectuer des tests d'intégration de bout en bout avec des requêtes réelles à un serveur en cours d'exécution, nous pouvons utiliser la méthodebindToServer:

WebTestClient testClient = WebTestClient
  .bindToServer()
  .baseUrl("http://localhost:8080")
  .build();

Nous pouvons tester unRouterFunction particulier en le passant à la méthodebindToRouterFunction:

RouterFunction function = RouterFunctions.route(
  RequestPredicates.GET("/resource"),
  request -> ServerResponse.ok().build()
);

WebTestClient
  .bindToRouterFunction(function)
  .build().get().uri("/resource")
  .exchange()
  .expectStatus().isOk()
  .expectBody().isEmpty();

Le même comportement peut être obtenu avec la méthodebindToWebHandler qui prend une instanceWebHandler:

WebHandler handler = exchange -> Mono.empty();
WebTestClient.bindToWebHandler(handler).build();

Une situation plus intéressante se produit lorsque nous utilisons la méthodebindToApplicationContext. Il prend unApplicationContext, analyse le contexte pour les beans de contrôleur et les configurations@EnableWebFlux.

Si nous injectons une instance desApplicationContext, un simple extrait de code peut ressembler à ceci:

@Autowired
private ApplicationContext context;

WebTestClient testClient = WebTestClient.bindToApplicationContext(context)
  .build();

Une approche plus courte consisterait à fournir un tableau de contrôleurs que nous voulons tester par la méthodebindToController. En supposant que nous avons une classeController et que nous l'avons injectée dans une classe nécessaire, nous pouvons écrire:

@Autowired
private Controller controller;

WebTestClient testClient = WebTestClient.bindToController(controller).build();

Après avoir construit un objetWebTestClient, toutes les opérations suivantes dans la chaîne vont être similaires auxWebClient jusqu'à la méthodeexchange (une façon d'obtenir une réponse), qui fournit leWebTestClient.ResponseSpec interface pour travailler avec des méthodes utiles comme lesexpectStatus,expectBody,expectHeader:

WebTestClient
  .bindToServer()
    .baseUrl("http://localhost:8080")
    .build()
    .post()
    .uri("/resource")
  .exchange()
    .expectStatus().isCreated()
    .expectHeader().valueEquals("Content-Type", "application/json")
    .expectBody().isEmpty();

6. Conclusion

Dans ce didacticiel, nous avons envisagé un nouveau mécanisme Spring amélioré pour effectuer des requêtes côté client: la classeWebClient.

Nous avons également examiné les avantages qu’il procure en effectuant tout le traitement des demandes.

Tous les extraits de code mentionnés dans l'article se trouvent dansour GitHub repository.