skip to content

WebFilter & WebFilterChain

WebFilter is the reactive analogue of a servlet filter: it returns Mono<Void>, can mutate the exchange, and is ordered with @Order. Interviewers ask how you carry context through it, since ThreadLocals are not an option.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is a Spring WebFlux `WebFilter`, and how does it relate to the servlet `Filter`?

level: juniorimportance: must knowfreq 68%

answer

  1. reactive twin of servlet Filter
  2. filter(exchange, chain) -> Mono<Void>
  3. return chain.filter(exchange) to continue
  4. register = just be a bean
  5. ServerWebExchange, not HttpServletRequest

basics

~10 s

WebFilter is WebFlux's reactive interface for intercepting every request/response, the non-blocking equivalent of a servlet Filter. Its filter(exchange, chain) method returns Mono<Void>, and you call chain.filter(exchange) to continue to the next filter or handler.

solid answer

~40 s

`org.springframework.web.server.WebFilter` is the reactive analogue of the servlet `Filter`. In a WebFlux (Reactor Netty / non-blocking) app there is no `HttpServletRequest`; instead a filter receives a `ServerWebExchange` (wrapping the reactive request and response) and a `WebFilterChain`. Its single method `Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain)` returns a `Mono<Void>` that completes when processing is done. To pass control down the chain you return `chain.filter(exchange)`. Register a filter simply by declaring it as a Spring bean; the framework discovers all `WebFilter` beans, orders them, and wraps every request — annotated controllers and functional endpoints alike. Typical uses: logging, adding/reading headers, tracing, correlation IDs, auth pre-checks. Crucially the work is non-blocking: you compose on the returned `Mono` rather than executing imperatively then returning.

code

java · 18 lines
java
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

@Component
public class RequestLoggingFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String method = exchange.getRequest().getMethod().name();
        String path = exchange.getRequest().getPath().value();
        // Continue the chain; log after it completes (non-blocking).
        return chain.filter(exchange)
                .doFinally(signal -> System.out.println(method + " " + path + " -> " + signal));
    }
}

go deeper

for a junior

Know it's the reactive Filter, the signature returns Mono<Void>, and you call chain.filter(exchange) to continue.

for a middle

Explain registration by bean, that it wraps every request, and the non-blocking 'return the Mono' idiom vs servlet's void return.

for a senior

Contrast with servlet Filter table, explain where it sits in the HttpHandler/WebHandler pipeline, and the no-blocking rule.

for a principal

Frame Spring Security reactive as a WebFilter chain; discuss why the reactive analogue needs a Mono<Void> return for backpressure/completion semantics.

## What it is `WebFilter` (full name `org.springframework.web.server.WebFilter`) is the interceptor abstraction of the **Spring WebFlux** reactive web stack. WebFlux is Spring's non-blocking web framework built on **Project Reactor** (`Mono`/`Flux`) and typically running on **Reactor Netty**, an event-loop server that handles many concurrent connections on a small thread pool without one-thread-per-request. Because there is no Servlet API in the pure-reactive stack, there is no `javax.servlet.Filter`, no `HttpServletRequest`, and no `FilterChain`. `WebFilter` fills that role. ## The interface ```java public interface WebFilter { Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain); } ``` - **`ServerWebExchange`** is the reactive container for one HTTP exchange. It exposes `getRequest()` (a `ServerHttpRequest`), `getResponse()` (a `ServerHttpResponse`), request attributes (`getAttributes()`), the `getSession()` (a `Mono<WebSession>`), and `getPrincipal()`. - **`WebFilterChain`** is the reactive analogue of the servlet `FilterChain`. Calling `chain.filter(exchange)` invokes the next filter, and eventually the `WebHandler` (which dispatches to your controller / functional endpoint). It returns a `Mono<Void>`. - The method returns **`Mono<Void>`** — a reactive stream that emits no value and simply signals *completion* (or error) when the whole downstream pipeline finishes. ## How you continue the chain Unlike a servlet filter where you call `chain.doFilter(req, res)` imperatively and then return, here you **return the Mono** produced by the chain: ```java return chain.filter(exchange); ``` Anything you compose onto that Mono (`.then(...)`, `.doFinally(...)`, `.contextWrite(...)`) runs as part of the reactive pipeline. ## Registration and discovery You do **not** register a `WebFilter` in `web.xml` or with `FilterRegistrationBean` (that is the servlet world). You just declare it as a Spring bean (`@Component`, or an `@Bean` method). At startup `WebHttpHandlerBuilder` collects all `WebFilter` beans, sorts them, and composes them into the reactive handler chain. They apply to **every** request handled by WebFlux — both `@RestController`/`@Controller` endpoints and `RouterFunction` (functional) endpoints. ## Where it sits The reactive pipeline is roughly: `HttpHandler` → `WebHandler` (the `WebFilter` chain lives here) → `DispatcherHandler` → `HandlerMapping`/`HandlerAdapter` → your controller. So `WebFilter`s wrap the whole handler dispatch. ## Relationship to the servlet Filter (side-by-side) | Servlet MVC | WebFlux | |---|---| | `javax.servlet.Filter` | `WebFilter` | | `doFilter(req, res, chain)` returns `void` | `filter(exchange, chain)` returns `Mono<Void>` | | `HttpServletRequest`/`Response` | `ServerHttpRequest`/`Response` via `ServerWebExchange` | | `FilterChain.doFilter(...)` | `chain.filter(exchange)` | | Blocking imperative code | Non-blocking reactive composition | (Servlet filters themselves are out of scope here — they belong to the MVC stack.) ## Common gotchas - **Never block.** Don't do JDBC/`RestTemplate`/`Thread.sleep` in a filter running on the event loop; it starves the server. - **You must return a Mono.** Forgetting to return `chain.filter(exchange)` (or returning `Mono.empty()` by accident) silently drops the request — the handler never runs. - **`ServerWebExchange` is effectively immutable**; to change the request/response you call `exchange.mutate()` and pass the new exchange down (covered in a later question). ## When to use Cross-cutting concerns that must see every request: logging/tracing, correlation IDs, request/response header manipulation, metrics, coarse auth pre-checks. Spring Security's reactive stack is itself implemented as a chain of `WebFilter`s.

  • How do you register a WebFilter — do you need FilterRegistrationBean?
    No. `FilterRegistrationBean` is for servlet filters. For a `WebFilter` you just expose it as a Spring bean (`@Component` or `@Bean`); `WebHttpHandlerBuilder` discovers and wires all `WebFilter` beans automatically.
  • What happens if your filter never calls chain.filter(exchange)?
    You short-circuit the request — the downstream filters and the handler never run. That's how you deliberately reject a request (return `exchange.getResponse().setComplete()` after setting a status), but doing it accidentally drops the request.

saying these in an interview costs you the question

  • Thinking WebFilter is the same class as the servlet Filter (or that you register it with FilterRegistrationBean)
  • Doing blocking I/O inside a WebFilter on the event loop
  • Not returning the Mono from chain.filter(exchange), assuming imperative return like doFilter

context

open as a page

Inside a `WebFilter`, how do you continue the chain versus short-circuit and return a response immediately (e.g. reject a request)?

level: middleimportance: must knowfreq 60%

basics

~10 s

To continue, return chain.filter(exchange). To short-circuit, do NOT call the chain: set the response status/headers on exchange.getResponse() and return exchange.getResponse().setComplete() (a Mono<Void> that finishes the exchange), so no downstream filter or handler runs.

open as a page

How do you control the execution order of multiple `WebFilter`s, and what does the order value mean?

level: middleimportance: should knowfreq 52%

basics

~20 s

Give each filter an order via the @Order annotation or by implementing Ordered. Spring sorts WebFilter beans by that value — lower value = higher precedence = runs earlier (and, because filters wrap each other, later on the way back out).

open as a page

How do you modify the request or response from a `WebFilter` given that `ServerWebExchange` is immutable? Explain `exchange.mutate()`.

level: seniorimportance: should knowfreq 46%

basics

~20 s

ServerWebExchange (and its request/response) are effectively immutable, so you can't set fields directly. Call exchange.mutate() to get a builder, apply changes (e.g. .request(r -> r.header(...))), call .build() to get a new exchange, and pass THAT to chain.filter(...).

open as a page

You need a `WebFilter` that measures total request latency and propagates a correlation ID to downstream reactive/logging code. What are the threading and context-propagation pitfalls, and how do you do it correctly?

level: principalimportance: should knowfreq 33%

basics

~20 s

Never use a ThreadLocal/MDC set imperatively — reactive work hops threads, so the value won't be there downstream. Measure latency by timing around the returned Mono (record start, then .doFinally on chain.filter). Propagate the correlation ID via the Reactor Context using .contextWrite(...), not a thread-local.

open as a page