Getting started with Spring WebClient (2023)

Eric Standley

What is Spring WebClient?

The Spring WebClient is a reactive HTTP library; It is the successor to the Spring RestTemplate which is now in maintenance mode. While RestTemplate was a synchronous blocking library, WebClient is an asynchronous non-blocking library. This guide also includes some information on using a Mono object from the Spring Reactive project, as this is key to how the WebClient works.

before you start

Things you must do before you start

  • JDK 11 (JDK 17 if you want to use the records in the GitHub sample code)
  • Springboot 2
  • Basic understanding of how Spring Boot apps work

This code and a sample test service to invokeat the top of GitHub.

Using WebClient

Set up the project and import dependencies

You can create a java project by going, generating a new Java project and including theSpring Reactive WebProject. Or you can manually set up a Java project and add the following to your pom.xml.

<parent><Group ID>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><Version>2.5.6</Version><relativerPfad/> <!-- Find parent from repository --></parent><dependencies> <dependency> <Group ID>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <Group ID>io. Project reactor</groupId> <artifactId>Reactor test</artifactId> <area>test</scope> </dependency></Dependencies>

Then create the following classes, which we will use throughout this guide:

@Componentspublic Class ReactiveWebClient { Private Finale WebClient webClient; public ReactiveWebClient() { }}

@Servicepublic Class ReactiveService { Private Finale ReactiveWebClient reactive web client; public ReactiveService(ReactiveWebClient reactive web client) { The.reactive web client = reactive web client; }}

@RestControllerpublic Class ReactiveExamplesController { Private Finale ReactiveService reactive service; Private Finale ReactiveWebClient reactive web client; public ReactiveExamples(ReactiveService reactive service, ReactiveWebClient reactive web client) { The.reactive service = reactive service; The.reactive web client = reactive web client; }}

Set up the web client

There are two ways to create a WebClient, the first using the create method, which has two versions: either an empty argument to set up a default WebClient, or one that takes the base URL that that WebClient calls ( this example uses the localhost url of the wait app in the code sample; you can use that or any other REST app you might have).

(Video) Spring WebClient Tutorial 2022

The.webClient = WebClient.create();//orThe.webClient = WebClient.create("http://localhost:12345");

There is also a more comprehensive builder method that you can use to set more default settings for the WebClient if you wish. For example, if you know you will only be using JSON and want to ensure that your Accept and Content headers are always set.

The.webClient = WebClient.Baumeister() .baseUrl(Characteristics.getHost()) .defaultHeader("Accept", media type.APPLICATION_JSON_VALUE, "content type", media type.APPLICATION_JSON_VALUE ).build();

For this guide just putthis.webClient = WebClient.create("URL des Dienstes");in the constructor ofReactiveWebClientClass.

make calls

Let's look at how to make a simple phone callwebClientwe just created. Place this method in yourReactiveWebClientClass.

public Mono<Returned item> callOne() { to return webClient.receive() .uri("wait1") .recall() .body to mono(Returned item.Class);}public record Returned item(UUID ID, line die Info) {}

Here you can see that the first method we call on our WebClient is the REST action we want to perform, in this case it's a.receive(). All common REST calls - GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS - have their own helper methods that can be called. The next method in the chain is this.uri(), which specifies what path we want to append to the base URL we used to set up this WebClient earlier. Next we have.recall(), where in the chain the WebClient actually makes the call, and we convert from methods that set up the call to methods that handle the response. I want to point this out instead of calling.recall(), there are also those.exchangeToMono()And.exchangeToFlux()methods. I won't go through these, but they are alternatives you can look up. Last is the.bodyToMono()method that gives us the payload we expect from our call. In this case, the invocation is expected to return a JSON payload that can be mapped toReturned itemrecord.

Before we go any further, we need to deal with this last part, as it is key to using the WebClient and is very different from how the old RestTemplate (or any synchronous HTTP client library) was handled. The best way to think about this is to view each invocation of the WebClient as a threaded process. So to compare the old to the new, look at the following code:

War Article = remainderTemplate.getForEntity(Characteristics.getHost() + "/" + end point, Returned item.Class);//Execution waits here until the call returns to the remote systemSystem.out of.println("ReturnedItem from the call: " + Article.toString());


War Article = webClient.receive().uri(API Properties.A_ENDPOINT).recall().body to mono(Returned item.Class);//The call to the remote system is still being processed, so the following line will be printed before the call has returnedSystem.out of.println("This is the mono that will return a ReturnedItem at some point in the future: " + Article.toString());

Instead of having our returned data from our REST call, we now get a Mono object that wraps our returned data and we still process without having our data yet. You may be wondering how to use the data from this call. Well, if you're used to the streaming API introduced in Java 8, you should notice the familiar method.Map()that exists on mono. If you've never used this at a basic level, it takes an object of one type and converts it to another. For example, take a code base that we want to convert theReturned itemthat comes from our call back in areturn objectto return to our customer. We can take the above method call and apply our logic to it. Place this method in theReactiveServiceClass.

public Mono<return object> simpleCall(){ to return reactive web client.callOne().Map(Returned item -> { War ids = List.von(Returned item.ID().toString()); to return neu return object(ids, Null); });}
(Video) 13 Using WebClient to make API calls - Spring Boot Microservices Level 1

Here you can see that the.Map()afunction<? Super T, ? expands R>Parameter that takes the ID from theReturned itemto create a new onereturn objectObject that has a list of IDs. Now that we have our object to return, you may be wondering how to get thatreturn objectfrom mono. Although there are several ways to get the value out of mono, for now we'll just leave it to Spring, and so our controller method can look like this. Place this method in theReactiveExamplesControllerClass.

@GetMapping("/defaultStatus")public Mono<return object> defaultStatus(){ to return reactive service.simpleCall();}

At this point, if we launch the dummy wait app and our sample reactive app, we should be able to make a simple call to our service that will return us a list of UUIDs. This should contain a single UUID as that is all we coded above.Getting started with Spring WebClient (1)

Handling custom returns and errors

Well, we got the basic call right, but if you've been developing for a while you'll find that there are many instances where we want to return some custom headers or handle errors to return something useful to our callers. Let's start with how to customize our return status and headers.

Custom statuses and headers

There are two ways to return a success status other than 200, which is Spring's default. The first and easiest way to do this would be using the Spring annotation@ResponseStatus(). Add this method to theReactiveExamplesControllerClass.

@GetMapping("/annotation status")@responsestatus(HTTPStatus.ACCEPTED)public Mono<return object> annotation status(){ to return reactive service.simpleCall();}

If we call this endpoint now, we can see that our return status has changed from the default of 200 OK to 202 ACCEPTEDGetting started with Spring WebClient (2)

The second way will once again reap the benefits.Map()method on the Mono class to return theResponseEntityObject. The bonus effect of this method is that we can also add additional REST data to the returned message. In this case, the following code adds a custom header in addition to switching up the status. Add this method to the againReactiveExamplesControllerClass.

@GetMapping("/responseStatus")public Mono<ResponseEntity<return object>> response status(){ War Mono = reactive service.simpleCall(); to return Mono.Map(return object -> { HttpHeader headers = neu HttpHeader(); headers.add to ("ETag", „ab435e9c88dcfbc355fe9a56061712f45b5d60b5a74b1ae8c5a13a6b04c3f834“); to return neu ResponseEntity<>(return object, headers, HTTPStatus.ACCEPTED); });}

Here you can see that we are adding themETagAdd a header to our reply and change the status to Accepted. There are many other options you can read about when creating your own response entity objectHere. Now if we name oursresponse statusendpoint, you'll see that we now have our ETag header as well.Getting started with Spring WebClient (3)

To deal with errors, let's add the following to oursReactiveWebClientClass

public Mono<Returned item> errorStatus(line end point){ to return webClient.receive().uri(end point).recall() .body to mono(Returned item.Class);}

And add the followingReactiveExamplesController:

(Video) How to Call a REST API using WebClient in Spring Boot

Private static Finale line endpoint400 = "400Errors";@GetMapping("/defaultHandle")public Mono<Returned item> defaultHandle(){ to return reactive web client.errorStatus(endpoint400);}

Now when we call our endpoint, you'll see that we get back an ugly 500 error, which doesn't tell us anything about our call to another service being a bad request.Getting started with Spring WebClient (4)

To correct this, to give us something useful, we need to set up two different parts. The first is the@ExceptionHandler()Annotation. This is a spring web class and while not specific to using the WebClient class, it makes life much easier when using the WebClient class. To use it, let's add the followingReactiveWebClientClass.

@ExceptionHandler(Exception.Class)public ResponseEntity<line> Others(Exception Exception){ to return ResponseEntity.Status(HTTPStatus.I_AM_A_TEAPOT.Wert()).build();}

Basically, this method tells Spring Boot that this controller is throwing a type exceptionexception.classCatch that and send back the instead of the standard error responseResponseEntitythat this method returns. In this case, since this is soughtexception.class, it acts as the default error handler. Now, if we run our code again, we should get back an empty body and a status of 418 I AM A TEAPOT.Getting started with Spring WebClient (5)

Here we can see that our 500 error with the default Spring error text has been replaced with our empty 418 error text. While this is a better way of handling things, you probably want to send back more informative errors. To do this we need to create our own exception, add the right oneexception handlerto our controller and update the WebClient's method chain to properly handle errors. First create theBadRequestExceptionClass.

public Class BadRequestException expanded Exception{ public BadRequestException() { } public BadRequestException(line News) { super(News); }}

Then add a new oneexception handlerfor theReactiveExamplesControllerGreat for handling our new exception.

@ExceptionHandler(BadRequestException.Class)public ResponseEntity<line> onBadRequest(BadRequestException BadRequestException){ to return ResponseEntity.Status(HTTPStatus.BAD REQUEST.Wert()).build();}

Now let's modify our WebClient call to handle our 400 error more cleanly. An important note here is that if our WebClient receives an HTTP status that isn't in the 200's, it will throw an exception, so you should always make sure you handle these exceptions.

public Mono<Returned item> errorStatus(line end point){ to return webClient.receive().uri(end point).recall() .onStatus(HTTPStatus::iserror, Answer -> switch (Answer.rawStatusCode()){ Fall 400 -> Mono.Mistake(neu BadRequestException("bad request made")); Fall 401, 403 -> Mono.Mistake(neu Exception("Authentication Error")); Fall 404 -> Mono.Mistake(neu Exception("Maybe not a mistake?")); Fall 500 -> Mono.Mistake(neu Exception("Server Error")); Standard -> Mono.Mistake(neu Exception("something went wrong")); }) .body to mono(Returned item.Class);}

Here we can apply the new method.onStatus(). This method takes a predicate that evaluates the response status code and, if it returns true, executes the function that is also sent to the method. In this example ours.onStatus()Handler runs when we get a status that is not in the 200s. As you can see we will return our new one when we get a 400 backBadRequestExceptionClass, otherwise we just return a generic exception with some custom labels. You'll also notice that we had to wrap our exceptions in aMono.error(). This is just a wrapper so the reactive library can propagate the error to any chain of Mono methods. Now when we call our 400 returning endpoint, we end up with:Getting started with Spring WebClient (6)

Now that we have error handling for failed requests, what about non-stateful exceptions? For example, suppose the request timed out and we don't have a status issue. In that case, there is one last method that can be used on the WebClient to handle these issues. First, let's add a new endpointReactiveExamplesControllerclass so we can set up a non-state error method.

(Video) Consume Rest service using Spring 5 WebClient (Reactive programming) | Java Techie

@GetMapping("/nonStatusError")public Mono<Returned item> nonStatusError(){ to return reactive web client.nonStatusError("unknown url", 9000, "/wooden path");}

Then add the following method in theReactiveWebClientClass.

public Mono<Returned item> nonStatusError(line host, int Harbor, line Away){ to return webClient.receive().uri(uriBuilder -> .onStatus(HTTPStatus::iserror, Answer -> switch (Answer.rawStatusCode()){ Fall 400 -> Mono.Mistake(neu BadRequestException("bad request made")); Fall 401, 403 -> Mono.Mistake(neu Exception("Authentication Error")); Fall 404 -> Mono.Mistake(neu Exception("Maybe not a mistake?")); Fall 500 -> Mono.Mistake(neu Exception("Server Error")); Standard -> Mono.Mistake(neu Exception("something went wrong")); }) .body to mono(Returned item.Class) .onErrorMap(throwable.Class, throwable -> neu Exception("Simple Exception"));}

We added those.onErrorMap()Method here, which in this example grabs everything of this typeThrowable.classand wrap that in whateverthrowableobject we want. Here we just wrap it in a normal exception but throw our specific error message fromsimple exception. If we change the default setting slightlyexception handlerIn the controller, we should be able to see this message in our return body.

@ExceptionHandler(Exception.Class)public ResponseEntity<line> Others(Exception Exception){ to return ResponseEntity.Status(HTTPStatus.I_AM_A_TEAPOT.Wert()).Body(Exception.getLocalizedMessage());}

So now the error we get from the call is sent back to us in the response to our request.Getting started with Spring WebClient (7)

And here we go. Instead of our timeout returning a generic Spring error, we were able to turn it into an error with the text "simple exception". But what happens if we change the call to return a 400 to us? I won't go into depth here, but you can play around with the WebClient call and see how you can change some of the call parameters to make this work.Getting started with Spring WebClient (8)

Uh oh, you can see here that while trying to catch the non-state errors, this solution caught ours as wellBadRequestErrorand changed it to the "simple exception" error that we set up to catch the non-state errors. Luckily there is another version of the.onErrorMap()Function that allows us to set up filtering to not change previous error handling.

public Mono<Returned item> nonStatusError(line host, int Harbor, line Away){ to return webClient.receive().uri(uriBuilder -> .onStatus(HTTPStatus::iserror, Answer -> switch (Answer.rawStatusCode()){ Fall 400 -> Mono.Mistake(neu BadRequestException("bad request made")); Fall 401, 403 -> Mono.Mistake(neu Exception("Authentication Error")); Fall 404 -> Mono.Mistake(neu Exception("Maybe not a mistake?")); Fall 500 -> Mono.Mistake(neu Exception("Server Error")); Standard -> Mono.Mistake(neu Exception("something went wrong")); }) .body to mono(Returned item.Class) .onErrorMap(predicate.not(BadRequestException.Class::isInstance), other exception -> neu Exception("other exception"));}

This version of.onErrorMap()first takes a predicate for the kind of bugs the map function is working on. This is a simple case when the current error is not aBadRequestExceptionThen convert the current error to a simple exception with the message "other exception".

Then add a final endpoint to oursReactiveExamplesControllerClass calling this new method with our 400 error endpoint and we should see that our specific error handling for 400 errors in our error map should not change at the end.

 @GetMapping("/statusErrorWithExceptionMapping") public Mono<ExtendedReturnedItem> statusErrorWithExceptionMapping(){ to return reactive web client.AvoidErrorSwallow("premises Host", "12345", endpoint400); }

And here's what we get:Getting started with Spring WebClient (9)

(Video) Calling REST from Java with Spring WebClient

As expected, although at the end of our call we have an error map, oursBadRequestExceptionmade it out of the call chain unchanged.

Wrap up

You should now have a good understanding of how to use the Spring WebClient class to make REST calls in your services. From here you should play around with the methods in the WebClient to see how to make either a PUT or POST call (hint: hit the.bodyValue()Method). Also, due to the move away from the normal synchronous style of yore, I'd recommend working with the Mono class to get a feel for how this works. It's a different way of thinking and requires new ways of testing components and understanding how to ensure you're getting the most out of using non-blocking code.


1. Spring Webclient : Lecture 1 - Perform HTTP GET, POST, PUT, DELETE operations using WebClient
(Code With Dilip)
2. Microservices Communication using WebClient
(Java Guides)
3. Spring Boot WebFlux | Project setup & understanding Mono & Flux Internal Workflow | JavaTechie
(Java Techie)
4. The elegant way of using WebClient in Spring Reactive to REST MicroService
(Muhammed Shakir)
5. @RestTemplate @WebClient RestTemplate and WebClient use with example
(WriteCodeWith Prince)
6. Spring Webflux Beginner Tutorial 2022
Top Articles
Latest Posts
Article information

Author: Frankie Dare

Last Updated: 01/05/2023

Views: 6415

Rating: 4.2 / 5 (73 voted)

Reviews: 80% of readers found this page helpful

Author information

Name: Frankie Dare

Birthday: 2000-01-27

Address: Suite 313 45115 Caridad Freeway, Port Barabaraville, MS 66713

Phone: +3769542039359

Job: Sales Manager

Hobby: Baton twirling, Stand-up comedy, Leather crafting, Rugby, tabletop games, Jigsaw puzzles, Air sports

Introduction: My name is Frankie Dare, I am a funny, beautiful, proud, fair, pleasant, cheerful, enthusiastic person who loves writing and wants to share my knowledge and understanding with you.