1. Overview

In this tutorial, we’re going to explore WebClient filters in Spring WebFlux, a functional, reactive web framework.

2. Request Filters

A filter can intercept, examine, and modify a client request (or response). Filters are very suitable for adding functionality to every single request since the logic stays in one place. Use cases include monitoring, modifying, logging, and authenticating client requests, just to mention a few.

A request has an ordered chain of zero or more filters.

In Spring Reactive, filters are instances of the functional interface ExchangeFilterFunction. The filter function has two parameters: the ClientRequest to modify and the next ExchangeFilterFunction.

Usually, a filter function returns by calling the next one in the filter chain:

ExchangeFilterFunction filterFunction = (clientRequest, nextFilter) -> {
    LOG.info("WebClient fitler executed");
    return nextFilter.exchange(clientRequest);
};

3. WebClient Filtering

After implementing a request filter, we have to “attach” it to the WebClient instance. This can be only done while creating the WebClient.

So then, let’s see how to create a WebClient. The first option is to invoke WebClient.create() with or without a base URL:

WebClient webClient = WebClient.create();

This, unfortunately, doesn’t allow to add a filter. The second option, then, is the one we’re looking for.

By using the WebClient.builder() we’re able to add filters:

WebClient webClient = WebClient.builder()
  .filter(filterFunction)
  .build();

4. A Custom Filter

Let’s start with a filter that counts the HTTP GET requests sent by the client.

The filter examines the request method and increases a “global” counter in case of a GET request:

ExchangeFilterFunction countingFunction = (clientRequest, nextFilter) -> {
    HttpMethod httpMethod = clientRequest.method();
    if (httpMethod == HttpMethod.GET) {
        getCounter.incrementAndGet();
    }
    return nextFilter.exchange(clientRequest);
};

The second filter we’ll define appends a version number to the request URL path. We utilize the ClientRequest.from() method to create a new request object from the current one and set the modified URL.

Subsequently, we continue executing the filter chain with the new modified request object:

ExchangeFilterFunction urlModifyingFilter = (clientRequest, nextFilter) -> {
    String oldUrl = clientRequest.url().toString();
    URI newUrl = URI.create(oldUrl + "/" + version);
    ClientRequest filteredRequest = ClientRequest.from(clientRequest)
      .url(newUrl)
      .build();
    return nextFilter.exchange(filteredRequest);
};

Next, let’s define a filter to log the methods of sent requests along with their URLs. These details are available in the request object.

All we have to do then is to print to some output stream:

ExchangeFilterFunction loggingFilter = (clientRequest, nextFilter) -> {
    printStream.print("Sending request " + clientRequest.method() + " " + clientRequest.url());
    return nextFilter.exchange(clientRequest);
};

5. A Standard Filter

Finally, let’s look into basic authentication – a very common use case of request filtering.

The helper class ExchangeFilterFunctions offers the basicAuthentication() filter function which takes care of adding the authorization header to the request.

As a result, we don’t need to define a filter for it:

WebClient webClient = WebClient.builder()
  .filter(ExchangeFilterFunctions.basicAuthentication(user, password))
  .build();

6. Conclusion

In this short article, we have explored filtering WebFlux clients in Spring.

As always, the code example can be found in over on GitHub.