Forår 5 WebClient

1. Oversigt

I denne vejledning skal vi undersøge Webklient, som er en reaktiv webklient, der blev introduceret i foråret 5.

Vi vil også se på WebTestClient, -en Webklient designet til brug i test.

2. Hvad er Webklient?

Kort fortalt, Webklient er en grænseflade, der repræsenterer hovedindgangsstedet til udførelse af webanmodninger.

Det blev oprettet som en del af Spring Web Reactive-modulet og vil erstatte klassikeren RestTemplate i disse scenarier. Derudover er den nye klient en reaktiv, ikke-blokerende løsning, der fungerer via HTTP / 1.1-protokollen.

Endelig har grænsefladen en enkelt implementering, StandardWebClient klasse, som vi vil arbejde med.

3. Afhængigheder

Da vi bruger en Spring Boot-applikation, har vi brug for spring-boot-starter-webflux afhængighed samt Reactor-projektet.

3.1. Bygning med Maven

Lad os tilføje følgende afhængigheder til pom.xml fil:

 org.springframework.boot spring-boot-starter-webflux org.projectreactor reactor-spring 1.0.1.RELEASE 

3.2. Bygning med Gradle

Med Gradle skal vi tilføje følgende poster til build.gradle fil:

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

4. Arbejde med Webklient

For at arbejde korrekt med klienten skal vi vide, hvordan man:

  • oprette en instans
  • fremsætte en anmodning
  • håndtere svaret

4.1. Oprettelse af en Webklient Instans

Der er tre muligheder at vælge imellem. Den første er at skabe en Webklient objekt med standardindstillinger:

WebClient client1 = WebClient.create (); 

Den anden mulighed er at starte en Webklient forekomst med en given base-URI:

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

Den tredje mulighed (og den mest avancerede) er at opbygge en klient ved hjælp af StandardWebClientBuilder klasse, som giver mulighed for fuld tilpasning:

WebClient client3 = WebClient .builder () .baseUrl ("// localhost: 8080"). DefaultCookie ("cookieKey", "cookieValue") .defaultHeader (HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUEVarue) .default. "," // localhost: 8080 ")) .build ();

4.2. Oprettelse af en Webklient Forekomst med timeouts

Ofte er standard HTTP-timeouts på 30 sekunder for langsomme til vores behov, så lad os se, hvordan vi konfigurerer dem til vores Webklient eksempel.

Kerneklassen vi bruger er TcpClient.

Der kan vi indstil forbindelsestimeout via ChannelOption.CONNECT_TIMEOUT_MILLIS værdi. Det kan vi også indstil læsning og skriv timeouts ved hjælp af a ReadTimeoutHandler og en WriteTimeoutHandler, henholdsvis:

TcpClient tcpClient = TcpClient .create () .option (ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000). DoOnConnected (forbindelse -> {connection.addHandlerLast (ny ReadTimeoutHandler (5000, TimeUnit.MILLISECONDS)); forbindelse.addHandler. MILLISEKONDER));}); WebClient-klient = WebClient.builder () .clientConnector (ny ReactorClientHttpConnector (HttpClient.from (tcpClient))) .build ();

Noter det mens vi kan ringe tiden er gået på vores klientforespørgsel er dette også et signal timeout, ikke en HTTP-forbindelse eller timeout for læse / skrive; det er en timeout for Mono / Flux-udgiveren.

4.3. Forberedelse af en anmodning

Først skal vi specificere en HTTP-metode til en anmodning ved at påkalde metode (HttpMethod metode) eller kalder dens genvejsmetoder som f.eks , stolpeog slet:

WebClient.UriSpec-anmodning1 = client3.method (HttpMethod.POST); WebClient.UriSpec-anmodning2 = client3.post ();

Det næste trin er at angive en URL. Vi kan give det til uri API som en Snor eller a java.net.URL eksempel:

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

Derefter kan vi indstille en anmodningsdel, indholdstype, længde, cookies eller overskrifter, hvis vi har brug for det.

For eksempel, hvis vi ønsker at indstille et anmodningsorgan, er der to tilgængelige måder: udfylde det med en BodyInserter eller delegere dette arbejde til en Forlægger:

WebClient.RequestHeadersSpec requestSpec1 = WebClient .create () .method (HttpMethod.POST) .uri ("/ resource") .body (BodyInserters.fromPublisher (Mono.just ("data")), String.class); WebClient.RequestHeadersSpec requestSpec2 = WebClient .create ("// localhost: 8080") .post () .uri (URI.create ("/ resource")) .body (BodyInserters.fromObject ("data"));

Det BodyInserter er en grænseflade, der er ansvarlig for at udfylde en ReaktivHttpOutputMessage body med en given outputmeddelelse og en kontekst, der blev brugt under indsættelsen. EN Forlægger er en reaktiv komponent, der har ansvaret for at levere et potentielt ubegrænset antal sekventerede elementer.

Den anden måde er legeme metode, som er en genvej til originalen body (BodyInserter inserter) metode.

For at lette processen med at udfylde en KropInserter, der er en BodyInserters klasse med en række nyttige hjælpemetoder:

BodyInserter inserter1 = BodyInserters .fromPublisher (abonnent :: onComplete, String.class); 

Det er også muligt med en MultiValueMap:

LinkedMultiValueMap map = nyt LinkedMultiValueMap (); map.add ("nøgle1", "værdi1"); map.add ("key2", "value2"); BodyInserter inserter2 = BodyInserters.fromMultipartData (kort); 

Eller ved at bruge et enkelt objekt:

BodyInserter inserter3 = BodyInserters.fromObject (nyt objekt ()); 

Når vi har indstillet kroppen, kan vi indstille overskrifter, cookies og acceptable medietyper. Værdier tilføjes til dem, der allerede er indstillet, når klienten startes.

Der er også yderligere support til de mest almindeligt anvendte headere som "Hvis-ingen-match", "Hvis-ændret-siden", "Accepter", og “Accept-Charset”.

Her er et eksempel på, hvordan disse værdier kan bruges:

WebClient.ResponseSpec respons1 = uri1 .body (inserter3). Header (HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .accept (MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML) .acceptCharset (Charset (Charset) ("). "*") .ifModifiedSince (ZonedDateTime.now ()) .hent ();

4.4. Få et svar

Den sidste fase er at sende anmodningen og modtage et svar. Dette kan gøres med enten udveksling eller den hente metode.

Disse metoder adskiller sig i returtyper; det udveksling metoden giver en ClientResponse sammen med dets status og overskrifter, mens hente metode er den korteste vej til at hente en krop direkte:

Strengrespons2 = anmodning1.udveksling () .blok () .bodyToMono (String.class) .blok (); Strengrespons3 = anmodning2 .hent () .bodyToMono (String.class) .block ();

Det er vigtigt at være opmærksom på bodyToMono metode, som vil kaste en WebClientException hvis statuskoden er 4xx (klientfejl) eller 5xx (Server Fejl). Vi bruger blok metode til Monos for at abonnere og hente faktiske data, der blev sendt med svaret.

5. Arbejde med WebTestClient

Det WebTestClient er det vigtigste indgangssted til test af WebFlux-serverendepunkter. Det har en meget lignende API til Webklient, og det delegerer det meste af arbejdet til en intern Webklient eksempel med hovedsagelig fokus på at levere en testkontekst. Det StandardWebTestClient klasse er en enkelt interface-implementering.

Klienten til test kan være bundet til en rigtig server eller arbejde med specifikke controllere eller funktioner.

5.1. Binding til en server

For at gennemføre end-to-end integrationstests med faktiske anmodninger til en kørende server kan vi bruge bindToServer metode:

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

5.2. Binding til en router

Vi kan teste en bestemt RouterFunktion ved at give det til bindToRouterFunction metode:

RouterFunction-funktion = RouterFunctions.route (RequestPredicates.GET ("/ resource"), anmodning -> ServerResponse.ok (). Build ()); WebTestClient .bindToRouterFunction (funktion) .build (). Get (). Uri ("/ resource") .exchange () .expectStatus (). IsOk () .expectBody (). IsEmpty (); 

5.3. Binding til en webhåndterer

Den samme adfærd kan opnås med bindToWebHandler metode, der tager en WebHandler eksempel:

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

5.4. Binding til en applikationskontekst

En mere interessant situation opstår, når vi bruger bindToApplicationContext metode. Det tager en ApplicationContext og analyserer konteksten for controller bønner og @EnableWebFlux konfigurationer.

Hvis vi injicerer en forekomst af ApplicationContext, et simpelt kodestykke kan se sådan ud:

@Autowired privat ApplicationContext-kontekst; WebTestClient testClient = WebTestClient.bindToApplicationContext (kontekst) .build (); 

5.5. Binding til en controller

En kortere tilgang ville være at give en række controllere, som vi vil teste af bindToController metode. Forudsat at vi har en Controller klasse, og vi injicerede den i en nødvendig klasse, kan vi skrive:

@Autowired privat controller controller; WebTestClient testClient = WebTestClient.bindToController (controller) .build (); 

5.6. At fremsætte en anmodning

Efter at have bygget en WebTestClient objekt, alle følgende operationer i kæden vil ligne Webklient indtil udveksling metode (en måde at få svar på), som giver WebTestClient.ResponseSpec interface til at arbejde med nyttige metoder som expectStatus, expectBodyog expectHeader:

WebTestClient .bindToServer () .baseUrl ("// localhost: 8080") .build () .post () .uri ("/ resource") .exchange () .expectStatus (). IsCreated () .expectHeader (). ValueEquals ("Content-Type", "application / json") .expectBody (). IsEmpty (); 

6. Konklusion

I denne artikel undersøgte vi Webklient, en ny forbedret fjedermekanisme til at stille anmodninger på klientsiden.

Vi kiggede også på de fordele, det giver ved at gennemgå konfigurationen af ​​klienten, forberede anmodningen og behandle svaret.

Alle de kodestykker, der er nævnt i artiklen, kan findes i vores GitHub-arkiv.