Spring 5 — WebClient and WebTestClient Guide | Code factory (2023)

I am offering:Link

WordPress blog:Link

WebClient is part of Spring 5's reactive web framework called Spring WebFlux. To use the WebClient, you must include itspring-webfluxunity in your work.

Adding a dependency to an existing Spring Boot project

If you have an existing Spring Boot project, you can add itspring-webfluxmodule by adding the following dependency to thepom.xmlfil -

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Note that you need the Spring Boot version2.x.xto use the Spring WebFlux module.

Create a new project from scratch

  1. I'm going to youhttp://start.spring.io.
  2. SerieAssociationandArtifactDetails.
  3. Add onReactive webdependency in the dependency section.
  4. clickManufactureto create and download the project.

Using external APIs using WebClient

we will consumeThe Github APIsusing the WebClient. We will use the WebClient to perform CRUD operations on the user's Github repositories.

Create an instance of WebClient

1. Create WebClient using itcabinet()method

You can create an instance of WebClient usingcabinet()factory method -

WebClient webClient = WebClient.create();

If you only use the API from a specific service, you can initialize the WebClient with the baseUrl of that service like:

WebClient webClient = WebClient.create("https://api.github.com");

2. Create a WebClient using the WebClient Builder

WebClient also comes with a builder that gives you a lot of customization options like filters, default headers, cookies, client logins, etc.

WebClient webClient = WebClient.builder()
.baseUrl("https://api.github.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.github.v3+json")
.defaultHeader(HttpHeaders.USER_AGENT, "Spring 5 WebClient")
.build();

Make a request using WebClient and retrieve the response

How to use WebClient to create aI GETrequestGithub's List Repositories API-

public Flux<GithubRepo> listGithubRepositories(String brugernavn, String token) {
returner webClient.get()
.uri("/user/repos")
.header("Autorisation", "Basic" + Base64Utils
.encodeToString((username + ":" + token).getBytes(UTF_8)))
.retrieve()
.bodyToFlux(GithubRepo.class);
}

We have a class namedGithubRepo(See below forclassfile) that validates the Github API response, the above function will return aFlowofGithubRepoobjects.

Note that I useBasic Github authenticationmechanism to call the APIs. It requires your github username and a personal access token that you can build fromhttps://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line#creating-a-tokenTHEhttps://github.com/settings/tokens.

Using the exchange() method to retrieve the response

Thefetch()method is the simplest way to get the response body. But if you want more control over the response, you can useexchange()method that accesses the setCustomer responseincluding all headers and body text -

public Flux<GithubRepo> listGithubRepositories(String brugernavn, String token) {
returner webClient.get()
.uri("/user/repos")
.header("Autorisation", "Basic" + Base64Utils
.encodeToString((username + ":" + token).getBytes(UTF_8)))
.exchange()
.flatMapMany(clientResponse -> clientResponse.bodyToFlux(GithubRepo.class));
}

Using parameters in the request URI

You can use parameters in the request URI and pass their values ​​separately touri()fashion. All parameters are surrounded by curly braces. The parameters will be automatically replaced by the web client before you send the request -

public Flux<GithubRepo> listGithubRepositories(String brugernavn, String token) {
returner webClient.get()
.uri("/user/repos?sort={sortField}&direction={sortDirection}",
"updated", "dec")
.header("Autorisation", "Basic" + Base64Utils
.encodeToString((username + ":" + token).getBytes(UTF_8)))
.retrieve()
.bodyToFlux(GithubRepo.class);
}

Using URIBuilder to construct the request URI

You can also gain full programmatic control over the request URI using aUriBuilderlike this -

public Flux<GithubRepo> listGithubRepositories(String brugernavn, String token) {
returner webClient.get()
.uri(uriBuilder -> uriBuilder.path("/user/repos")
.queryParam("sort", "updated")
.queryParam("retning", "desc")
.build())
.header("Autorisation", "Basic" + Base64Utils
.encodeToString((username + ":" + token).getBytes(UTF_8)))
.retrieve()
.bodyToFlux(GithubRepo.class);
}

Sends request text to WebClient requests

If you have the request body in the form of aWhenshe hasFlow, then you can pass it on directly tobody()method in WebClient, otherwise you can just create a Mono/Flux from an object and send it like this -

public Mono<GithubRepo> createGithubRepository(String brugernavn, String token,
RepoRequest createRepoRequest) {
returner webClient.post()
.uri("/user/repos")
.body(Mono.just(createRepoRequest), RepoRequest.class)
.header("Autorisation", "Basic" + Base64Utils
.encodeToString((username + ":" + token).getBytes(UTF_8)))
.retrieve()
.bodyToMono(GithubRepo.class);
}

If you have a real value instead of aPublisher(Flow/When), can you usesyncBody()shortcut method to send the request text -

public Mono<GithubRepo> createGithubRepository(String brugernavn, String token,
RepoRequest createRepoRequest) {
returner webClient.post()
.uri("/user/repos")
.syncBody(createRepoRequest)
.header("Autorisation", "Basic" + Base64Utils
.encodeToString((username + ":" + token).getBytes(UTF_8)))
.retrieve()
.bodyToMono(GithubRepo.class);
}

Finally, you can use various factory methods provided byBodyInsertersclass to construct oneBodyInserterobject and pass it tobody()method. ThatBodyInsertersThe class contains methods to create oneBodyInserterfrom aObject,Publisher,Resource,FormData,MultipartDataetc -

public Mono<GithubRepo> createGithubRepository(String brugernavn, String token,
RepoRequest createRepoRequest) {
returner webClient.post()
.uri("/user/repos")
.body(BodyInserters.fromObject(createRepoRequest))
.header("Autorisation", "Basic" + Base64Utils
.encodeToString((username + ":" + token).getBytes(UTF_8)))
.retrieve()
.bodyToMono(GithubRepo.class);
}

Add filter functions

WebClient supports request filtering using aExchangeFilterFunction. You can use the filter functions to intercept and modify the request in any way. For example, you can use a filter function to add aAuthorizationheader for each request or to record the details of each request.

TheExchangeFilterFunctiontakes two arguments -

  1. TheClientRequestand
  2. The nextExchangeFilterFunctionin the filter chain.

It can change thatClientRequestand call the next oneExchange filter functionin the filter chain to advance to the next filter or return the modified oneClientRequestdirectly to block the filter chain.

1. Add Basic Authentication using a filter function

In all the examples above, we include aAuthorizationheader for basic authentication with the Github API. Since this is something common to all requests, you can add this logic to a filter function when you create itWebClient.

TheExchaneFilterFunctionsThe API already includes a filter for basic authentication. You can use it like this -

WebClient webClient = WebClient.builder()
.baseUrl(GITHUB_API_BASE_URL)
.defaultHeader(HttpHeaders.CONTENT_TYPE, GITHUB_V3_MIME_TYPE)
.filter(ExchangeFilterFunctions
.basicAuthentication(username, token))
.build();

Now you don't need to add itAuthorizationheader in each request. The filter function intercepts each WebClient request and adds this header.

2. Log all requests using a filter function

Let's look at an example of a customExchangeFilterFunction. We will write a filter function to intercept and log each request -

WebClient webClient = WebClient.builder()
.baseUrl(GITHUB_API_BASE_URL)
.defaultHeader(HttpHeaders.CONTENT_TYPE, GITHUB_V3_MIME_TYPE)
.filter(ExchangeFilterFunctions
.basicAuthentication(username, token))
.filter(logRequest())
.build();

Here is its applicationlogRequest()filter function -

private ExchangeFilterFunction logRequest() {
return ( clientRequest , next ) -> {
logger.info("Request: {} {}", clientRequest.method(), clientRequest.url());
clientRequest.headers()
.forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value)));
return next.exchange(clientRequest);
};
}

3. Using the RequestProcessor() and ofResponseProcessor() factory methods to create filters

The ExchangeFilterFunction API contains two named factory methodsofRequestProcessor()andofResponseProcessor()to create filter functions that intercept the request and the response respectively.

ThelogRequest()The filter function we created in the previous section can be created using theofRequestProcessor()factory method like this -

private ExchangeFilterFunction logRequest() {
ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
logger.info("Request: {} {}", clientRequest.method(), clientRequest.url());
clientRequest.headers()
.forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value)));
returner Mono.just(clientRequest);
});
}

If you want to intercept the WebClient response, you can useofResponseProcessor()method to create a filter function like this -

private ExchangeFilterFunction logResposneStatus() {
returner ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
logger.info("Svarstatus {}", clientResponse.statusCode());
returner Mono.just(clientResponse);
});
}

WebClient error handling

Thefetch()method on WebClient throws aWebClientResponseExceptionevery time a response is received with a 4xx or 5xx status code.

You can customize it usingonStatus()such methods -

public Flux<GithubRepo> listGithubRepositories() {
returner webClient.get()
.uri("/user/repos?sort={sortField}&direction={sortDirection}",
"updated", "dec")
.retrieve()
.onStatus(HttpStatus::is4xxClientError, clientResponse ->
Mono.error(new MyCustomClientException())
)
.onStatus(HttpStatus::is5xxServerError, clientResponse ->
Mono.error(ny MyCustomServerException())
)
.bodyToFlux(GithubRepo.class);
}

Note that, on the other handfetch()method, hexchange()The method does not throw exceptions in case of 4xx or 5xx responses. You must check the status codes yourself and handle them as you wish.

Handle WebClientResponseExceptions using a@ExceptionHandlerinside the controller

You can use one@ExceptionHandlerinside your controller to operate itWebClientResponseExceptionand return an appropriate response to clients like this -

@ExceptionHandler(WebClientResponseException.class)
public ResponseEntity<String> handleWebClientResponseException(WebClientResponseException ex) {
logger.error("Fejl fra WebClient - Status {}, Body {}", ex.getRawStatusCode(), ex.getResponseBodyAsString(), ex);
returner ResponseEntity.status(ex.getRawStatusCode()).body(ex.getResponseBodyAsString());
}

Rest API testing using Spring 5 WebTestClient

WebTestClient contains request methods similar to WebClient. Additionally, it contains methods to control the response state, header, and body. You can also use assertion libraries such asAssertJwith WebTestClient.

See the following example to learn how to run rest API tests using WebTestClient −

package com.example;import org.assertj.core.api.Assertions;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import com.example.payload.GithubRepo;
import com.example.payload.RepoRequest;
import reactor.core.publisher.Mono;/**
* @author code.factory
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class Spring5WebclientApplicationTests {
@Autowired
privat WebTestClient webTestClient;
@Try
public void test1CreateGithubRepository() {
RepoRequest repoRequest = new RepoRequest("test-webclient-repository",
"Repository created for testing WebClient");
webTestClient.post().uri("/api/repos")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.body(Mono.just(repoRequest), RepoRequest.class)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody()
.jsonPath("$.name").isNotEmpty()
.jsonPath("$.name").isEqualTo("test-webclient-repository");
}
@Try
public void test2GetAllGithubRepositories() {
webTestClient.get().uri("/api/repos")
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBodyList(GithubRepo.class);
}
@Try
public void test3GetSingleGithubRepository() {
webTestClient.get().uri("/api/repos/{repo}", "test-webclient-repository")
.exchange()
.expectStatus().isOk()
.expectBody()
.consumeWith(response -> Assertions.assertThat(response.getResponseBody()).isNotNull());
}
@Try
public void test4EditGithubRepository() {
RepoRequest newRepoDetails = new RepoRequest("updated-webclient-repository", "Updated name and description");
webTestClient.patch().uri("/api/repos/{repo}", "test-webclient-repository")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.body(Mono.just(newRepoDetails), RepoRequest.class)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody()
.jsonPath("$.name").isEqualTo("updated-webclient-repository");
}
@Try
public void test5DeleteGithubRepository() {
webTestClient.delete().uri("/api/repos/{repo}", "updated-webclient-repository")
.exchange()
.expectStatus().isOk();
}
}
Spring 5 — WebClient and WebTestClient Guide | Code factory (1)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring5-webclient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring5-webclient</name>
<description>Demoprojekt til Spring Boot 5 WebClient</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reaktortest</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validerings-API</artifactId>
<version>2.0.1.Final</version>
</dependency>
</dependencies>
<construction>
<further>
<further>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

application properties

app.github.username=GIT_USERNAME
app.github.token=GIT_TOKEN

Spring5WebclientApplication.java

package com.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author code.factory
*
*/
@SpringBootApplication
public class Spring5WebclientApplication {
public static void main(String[] args) {
SpringApplication.run(Spring5WebclientApplication.class, args);
}
}

GithubClient.java

package com.example;import com.example.config.AppProperties;
import com.example.payload.RepoRequest;
import com.example.payload.GithubRepo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
εισαγωγή org.springframework.web.reactive.function.client.ExchangeFilterFunction;
εισαγωγή org.springframework.web.reactive.function.client.ExchangeFilterFunctions;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* @author code.factory
*
*/
@Service
public class GithubClient {
private static final String GITHUB_V3_MIME_TYPE = "application/vnd.github.v3+json";
private static final String GITHUB_API_BASE_URL = "https://api.github.com";
private static final String USER_AGENT = "Forår 5 WebClient";
private static final Logger Logger = LoggerFactory.getLogger(GithubClient.class);
private final WebClient webClient;@Autowired
public GithubClient(AppProperties appProperties) {
this.webClient = WebClient.builder().baseUrl(GITHUB_API_BASE_URL)
.defaultHeader(HttpHeaders.CONTENT_TYPE, GITHUB_V3_MIME_TYPE)
.defaultHeader(HttpHeaders.USER_AGENT, USER_AGENT)
.filter(ExchangeFilterFunctions.basicAuthentication(appProperties.getGithub().getUsername(),
appProperties.getGithub().getToken()))
.filter(logRequest()).build();
}
public Flux<GithubRepo> listGithubRepositories() {
return webClient.get().uri("/user/repos?sort={sortField}&direction={sortDirection}", "updated", "desc")
.exchange()
.flatMapMany(clientResponse -> clientResponse.bodyToFlux(GithubRepo.class));
}
δημόσιο Mono<GithubRepo> createGithubRepository(RepoRequest createRepoRequest) {
returner webClient.post().uri("/user/repos")
.body(Mono.just(createRepoRequest), RepoRequest.class)
.retrieve()
.bodyToMono(GithubRepo.class);
}
public Mono<GithubRepo> getGithubRepository(String ejer, String repository) {
return webClient.get().uri("/repos/{owner}/{repo}", ejer, repo)
.retrieve()
.bodyToMono(GithubRepo.class);
}
public Mono<GithubRepo> editGithubRepository(String ejer, String repository, RepoRequest editRepoRequest) {
return webClient.patch().uri("/repos/{owner}/{repo}", ejer, repo)
.body(BodyInserters.fromObject(editRepoRequest))
.retrieve()
.bodyToMono(GithubRepo.class);
}
public Mono<Void> deleteGithubRepository(String ejer, String repository) {
return webClient.delete().uri("/repos/{owner}/{repo}", ejer, repo)
.retrieve()
.bodyToMono(Void.class);
}
private ExchangeFilterFunction logRequest() {
return ( clientRequest , next ) -> {
logger.info("Request: {} {}", clientRequest.method(), clientRequest.url());
clientRequest.headers()
.forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value)));
return next.exchange(clientRequest);
};
}
}

GithubController.java

package com.example;import com.example.config.AppProperties;
import com.example.payload.RepoRequest;
import com.example.payload.GithubRepo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.validation.Valid;/**
* @author code.factory
*
*/
@RestController
@RequestMapping("/api")
public class GithubController {
@Autowired
private GithubClient githubClient;
@Autowired
private AppProperties appProperties;
private static final Logger Logger = LoggerFactory.getLogger(GithubController.class);@GetMapping("/rest")
public Flux<GithubRepo> listGithubRepositories() {
returner githubClient.listGithubRepositories();
}
@PostMapping("/repos")
δημόσιο Mono<GithubRepo> createGithubRepository(@RequestBody RepoRequest repoRequest) {
returner githubClient.createGithubRepository(repoRequest);
}
@GetMapping("/repos/{repo}")
public Mono<GithubRepo> getGithubRepository(@PathVariable String repo) {
returner githubClient.getGithubRepository(appProperties.getGithub().getUsername(), repo);
}
@PatchMapping("/repos/{repo}")
public Mono<GithubRepo> editGithubRepository(@PathVariable String repo, @Valid @RequestBody RepoRequest repoRequest) {
επιστροφή githubClient.editGithubRepository(appProperties.getGithub().getUsername(), repo, repoRequest);
}
@DeleteMapping("/repos/{repo}")
public Mono<Void> deleteGithubRepository(@PathVariable String repo) {
returner githubClient.deleteGithubRepository(appProperties.getGithub().getUsername(), repo);
}
@ExceptionHandler(WebClientResponseException.class)
public ResponseEntity<String> handleWebClientResponseException(WebClientResponseException ex) {
logger.error("Fejl fra WebClient - Status {}, Body {}", ex.getRawStatusCode(), ex.getResponseBodyAsString(), ex);
returner ResponseEntity.status(ex.getRawStatusCode()).body(ex.getResponseBodyAsString());
}
}

AppProperties.java

package com.example.config;εισαγωγή org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author code.factory
*
*/
@Configuration
@ConfigurationProperties(prefix="app")
public class AppProperties {
private final Github github = new Github();
public static class Github {
private String username;
private String token;
public String getUsername() {
return username.
}
public void setUsername(String username) {
this.username = username;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
public Github getGithub() {
return github;
}
}

GithubRepo.java

pakke com.example.payload;import com.fasterxml.jackson.annotation.JsonProperty;/**
* @author code.factory
*
*/
public class GithubRepo {
private Long id;
private String name;@JsonProperty("full_name")
private String fullName;
private String description;@JsonProperty("private")
private string is Private.
@JsonProperty("fork")
private String isFork;
private string URL;@JsonProperty("html_url")
private string htmlURL;
@JsonProperty("git_url")
private String gitUrl;
@JsonProperty("forks_count")
private Long forksCount;
@JsonProperty("stargazers_count")
private Long stargazersCount;
@JsonProperty("watchers_count")
private Long observersCount;
public Long getId() {
return-it;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getIsPrivate() {
The return is private.
}
public void setIsPrivate(String erPrivate) {
this.isPrivate = erPrivat;
}
public String getIsFork() {
return isFork;
}
public void setIsFork(String isFork) {
this.isFork = isFork;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getHtmlUrl() {
returner html-URL;
}
public void setHtmlUrl(String htmlUrl) {
this.htmlUrl = htmlUrl;
}
public String getGitUrl() {
returner gitUrl;
}
public void setGitUrl(String gitUrl) {
this.gitUrl = gitUrl;
}
public Long getForksCount() {
returgaflerCount;
}
public void setForksCount(Long forksCount) {
this.forksCount = forksCount;
}
public Long getStargazersCount() {
return stargazersCount;
}
public void setStargazersCount(Long stargazersCount) {
this.stargazersCount = stargazersCount;
}
public Long getWatchersCount() {
return observersCount;
}
public void setWatchersCount(Long watchersCount) {
this.watchersCount = watchersCount;
}
}

RepoRequest.java

pakke com.example.payload;import javax.validation.constraints.NotBlank;import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author code.factory
*
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RepoRequest {
@NotBlank
private String name;
private String description;@JsonProperty("private")
private boolean isPrivate;
public RepoRequest() {}public RepoRequest(@NotBlank String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getPrivate() {
The return is private.
}
public void setPrivate(Boolean aPrivate) {
erPrivat = aPrivat;
}
@Trampe
public String toString() {
return "RepoRequest [name=" + name + ", description=" + description + ", isPrivate=" + isPrivate + "]";
}
}

Spring5WebclientApplicationTests.java

package com.example;import org.assertj.core.api.Assertions;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import com.example.payload.GithubRepo;
import com.example.payload.RepoRequest;
import reactor.core.publisher.Mono;/**
* @author code.factory
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class Spring5WebclientApplicationTests {
@Autowired
privat WebTestClient webTestClient;
@Try
public void test1CreateGithubRepository() {
RepoRequest repoRequest = new RepoRequest("test-webclient-repository",
"Repository created for testing WebClient");
webTestClient.post().uri("/api/repos")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.body(Mono.just(repoRequest), RepoRequest.class)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody()
.jsonPath("$.name").isNotEmpty()
.jsonPath("$.name").isEqualTo("test-webclient-repository");
}
@Try
public void test2GetAllGithubRepositories() {
webTestClient.get().uri("/api/repos")
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBodyList(GithubRepo.class);
}
@Try
public void test3GetSingleGithubRepository() {
webTestClient.get().uri("/api/repos/{repo}", "test-webclient-repository")
.exchange()
.expectStatus().isOk()
.expectBody()
.consumeWith(response -> Assertions.assertThat(response.getResponseBody()).isNotNull());
}
@Try
public void test4EditGithubRepository() {
RepoRequest newRepoDetails = new RepoRequest("updated-webclient-repository", "Updated name and description");
webTestClient.patch().uri("/api/repos/{repo}", "test-webclient-repository")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.body(Mono.just(newRepoDetails), RepoRequest.class)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody()
.jsonPath("$.name").isEqualTo("updated-webclient-repository");
}
@Try
public void test5DeleteGithubRepository() {
webTestClient.delete().uri("/api/repos/{repo}", "updated-webclient-repository")
.exchange()
.expectStatus().isOk();
}
}
Spring 5 — WebClient and WebTestClient Guide | Code factory (2)
Spring 5 — WebClient and WebTestClient Guide | Code factory (3)
Spring 5 — WebClient and WebTestClient Guide | Code factory (4)
Spring 5 — WebClient and WebTestClient Guide | Code factory (5)
Spring 5 — WebClient and WebTestClient Guide | Code factory (6)

FAQs

How do you pass query parameters in WebClient? ›

To pass single-value query parameters, create a path of the base resource URI and then use the queryParam() method to append key-value pairs.

What is the default Readtimeout for WebClient? ›

java example code the WebClient build using the default builder without any specific configuration. Hence it falls back to the default connect and read timeout, which is 30 seconds each. Modern applications do not wait for 30 seconds for anything.

How to test WebClient in Spring Boot? ›

Testing Spring Boot WebClient With MockWebServer
  1. Related Video. ...
  2. What is Spring WebClient? ...
  3. Responsibilities of a WebClient. ...
  4. Don't Try To Mock WebClient. ...
  5. Write an Integration Test With MockWebServer. ...
  6. Verify the Request. ...
  7. Verify Input Mapping and Request Serialization. ...
  8. Verify Response Deserialization and Output Mapping.
Jan 16, 2023

Should I use RestTemplate or WebClient? ›

Conclusion

RestTemplate uses Java Servlet API and is therefore synchronous and blocking. Conversely, WebClient is asynchronous and will not block the executing thread while waiting for the response to come back. The notification will be produced only when the response is ready. RestTemplate will still be used.

How to pass multiple parameters in web service? ›

Pass Multiple Parameters in URL in Web API
  1. In the "Solution Explorer".
  2. Expand the "Views" folder.
  3. Select "Home" -> "Index. cshtml". Add the following code: @{ ViewBag. Title = "Index"; } <h3>Passing multiple parameter in URL</h3> @using (Html. BeginForm("index", "Home")) { <a href="/home/MultipleParameter/?
Dec 11, 2020

How to pass URL and query parameters in Spring REST client? ›

To perform a GET request with parameters using the RestTemplate in Spring, you can use the getForObject() method and pass it a URL with placeholders for the parameters, as well as a map of the parameter values. This will send a GET request to the URL http://example.com/users?id=123&name=Alice and print the response.

What is the factory default timeout for Httpclient? ›

The default value is 100,000 milliseconds (100 seconds).

What is the default connection pool Webclient? ›

By default, the TCP client uses a “fixed” connection pool with 500 as the maximum number of the channels and 45s as the acquisition timeout.

What is the default value of Smtpclient timeout? ›

Property Value

The default value is 100,000 (100 seconds).

How to test Spring code? ›

Testing in Spring Boot
  1. Overview. ...
  2. Project Setup. ...
  3. Maven Dependencies. ...
  4. Integration Testing With @SpringBootTest. ...
  5. Test Configuration With @TestConfiguration. ...
  6. Mocking With @MockBean. ...
  7. Integration Testing With @DataJpaTest. ...
  8. Unit Testing With @WebMvcTest.
Apr 18, 2023

What is the difference between spring boot WebClient and Feignclient? ›

While the Feign client creates a thread for each request and blocks it until it receives a response, the WebClient executes the HTTP request and adds a “waiting for response” task into a queue.

How to authenticate web API Spring Boot? ›

Spring Boot and Authorization
  1. Users will start by authenticating with a username and password managed by Auth0.
  2. Once authenticated, the client will receive a JWT representing an access token.
  3. The client will include the access token in the authorization header of every request to a secure endpoint.
Oct 7, 2021

Is RestTemplate deprecated since Spring 5? ›

RestTemplate provides a synchronous way of consuming Rest services, which means it will block the thread until it receives a response. RestTemplate is deprecated since Spring 5 which means it's not really that future proof. First, we create a Spring Boot project with the spring-boot-starter-web dependency.

Is WebClient asynchronous or synchronous? ›

On the other hand, WebClient is asynchronous and non-blocking, supporting reactive programming and event-driven architecture. It can perform similar synchronous operations as RestTemplate by using the block() method.

Which is better HTTP client or WebClient? ›

Further, unlike WebClient, HttpClient lacks support for progress reporting and custom URI schemes. Although HttpClient doesn't support FTP, mocking and testing HttpClient is easier. All I/O bound methods in HttpClient are asynchronous, and you can use the same HttpClient instance to make concurrent requests as well.

How to get multiple parameters in HTTP URL? ›

Any word after the question mark (?) in a URL is considered to be a parameter which can hold values. The value for the corresponding parameter is given after the symbol "equals" (=). Multiple parameters can be passed through the URL by separating them with multiple "&".

How to pass multiple query parameters in restful web service? ›

Query parameters are passed after the URL string by appending a question mark followed by the parameter name , then equal to (“=”) sign and then the parameter value. Multiple parameters are separated by “&” symbol.

What is the difference between FromRoute and FromQuery? ›

[FromQuery] - Gets values from the query string. [FromRoute] - Gets values from route data. [FromForm] - Gets values from posted form fields. [FromBody] - Gets values from the request body.

How do I send URL and query parameters together? ›

Query parameters are a defined set of parameters attached to the end of a url. They are extensions of the URL that are used to help define specific content or actions based on the data being passed. To append query params to the end of a URL, a '? ' Is added followed immediately by a query parameter.

What is the difference between PathVariable and RequestParam? ›

Just remember that, @RequestParam is used to capture query parameters or form parameters from the URL, while @PathVariable is used to capture values from the URL path. They have different syntax, usage, and behavior in handling URL parameters in Spring MVC applications.

What is the difference between QueryParam and RequestParam? ›

@QueryParam is a JAX-RS framework annotation and @RequestParam is from Spring. QueryParam is from another framework and you are mentioning Spring. @Flao wrote that @RequestParam is from Spring and that should be used in Spring MVC.

How do you pass a query parameter? ›

Query parameters are a defined set of parameters attached to the end of a url. They are extensions of the URL that are used to help define specific content or actions based on the data being passed. To append query params to the end of a URL, a '? ' Is added followed immediately by a query parameter.

Which is the correct way of passing query parameters? ›

To pass in parameter values, simply append them to the query string at the end of the base URL. In the above example, the view parameter script name is viewParameter1.

How do you pass parameters in select query? ›

Create a parameter query
  1. Create a select query, and then open the query in Design view.
  2. In the Criteria row of the field you want to apply a parameter to, enter the text that you want to display in the parameter box, enclosed in square brackets. ...
  3. Repeat step 2 for each field you want to add parameters to.

How to pass query parameters in REST API? ›

To configure a REST API to pass query string parameters to a backend AWS Lambda function, use a Lambda custom integration. To pass query string parameters to an HTTP endpoint, use an HTTP custom integration. Important: Make sure that you supply the input data as the integration request payload.

References

Top Articles
Latest Posts
Article information

Author: Ray Christiansen

Last Updated: 12/10/2023

Views: 6173

Rating: 4.9 / 5 (69 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Ray Christiansen

Birthday: 1998-05-04

Address: Apt. 814 34339 Sauer Islands, Hirtheville, GA 02446-8771

Phone: +337636892828

Job: Lead Hospitality Designer

Hobby: Urban exploration, Tai chi, Lockpicking, Fashion, Gunsmithing, Pottery, Geocaching

Introduction: My name is Ray Christiansen, I am a fair, good, cute, gentle, vast, glamorous, excited person who loves writing and wants to share my knowledge and understanding with you.