skip to content

What are RequestPredicates in Spring WebFlux functional endpoints, and how do they connect an incoming request to a HandlerFunction?

level: juniorimportance: must knowfreq 55%

answer

  1. predicate = boolean test over ServerRequest
  2. route(predicate, handler)
  3. GET = method(GET).and(path)
  4. first match wins
  5. HandlerFunction -> Mono<ServerResponse>

basics

~10 s

A RequestPredicate is a condition that tests an incoming request (its method, path, headers). In RouterFunctions.route(predicate, handler), if the predicate matches, that HandlerFunction runs and returns the response.

solid answer

~30 s

In WebFlux's functional model you don't use @RequestMapping; instead you build a RouterFunction from pairs of (RequestPredicate, HandlerFunction). A RequestPredicate is a boolean test over a ServerRequest — created by static factories in the RequestPredicates class such as GET, POST, path, accept, contentType, and queryParam. You wire them with RouterFunctions.route(predicate, handler). When a request arrives, the RouterFunctionMapping evaluates predicates in declaration order; the first whose predicate returns true wins, and its HandlerFunction — a function taking a ServerRequest and returning Mono<ServerResponse> — is invoked. GET("/users") is really a convenience for method(GET).and(path("/users")). Unmatched requests fall through, ultimately producing a 404.

code

java · 17 lines
java
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import org.springframework.web.reactive.function.server.*;

@Configuration
public class UserRoutes {
    @Bean
    RouterFunction<ServerResponse> routes(UserHandler handler) {
        return route(GET("/users/{id}"), handler::getUser);
    }
}

// handler
Mono<ServerResponse> getUser(ServerRequest req) {
    String id = req.pathVariable("id");
    return ServerResponse.ok().bodyValue("user " + id);
}

go deeper

for a junior

Know predicate = condition, route() pairs it with a handler, first match wins.

for a middle

Explain GET as method().and(path()) and the factory methods available.

for a senior

Discuss declaration-order matching and shadowing risks.

for a principal

Frame functional vs annotation trade-offs and testability of routes-as-values.

**The two styles.** Spring WebFlux offers two ways to define web endpoints: the annotation model (`@RestController` + `@GetMapping`) and the *functional* model. The functional model expresses routing as data: a `RouterFunction` is essentially a list of `(RequestPredicate, HandlerFunction)` pairs. **RequestPredicate.** `RequestPredicate` is an interface in `org.springframework.web.reactive.function.server` with a single core method `boolean test(ServerRequest request)`. You almost never implement it by hand; instead you use the static factory methods on the `RequestPredicates` class: - `RequestPredicates.GET(String pattern)`, `POST`, `PUT`, `DELETE`, `PATCH` — match HTTP method **and** path. - `RequestPredicates.method(HttpMethod)` — match method only. - `RequestPredicates.path(String pattern)` — match path only, using a `PathPattern` (supports `{var}`, `*`, `**`). - `RequestPredicates.accept(MediaType...)` — match the request's `Accept` header (what the client wants back). - `RequestPredicates.contentType(MediaType...)` — match the request's `Content-Type` header (what the client is sending). - `RequestPredicates.queryParam(String name, String value)` or `queryParam(String name, Predicate<String>)`. - `headers(Predicate<ServerRequest.Headers>)`, `pathExtension`, etc. **HandlerFunction.** A `HandlerFunction<ServerResponse>` is a functional interface: `Mono<ServerResponse> handle(ServerRequest request)`. It reads from the `ServerRequest` (path variables, query params, body) and returns a `Mono<ServerResponse>` you build with `ServerResponse.ok()...`. **How routing happens.** You register the `RouterFunction` as a `@Bean`. At startup a `RouterFunctionMapping` picks it up. On each request, predicates are tested **in the order routes were declared**; the **first** match wins and its handler runs. If nothing matches, the request falls through the composed router and yields a 404. **GET is sugar.** `GET("/x")` is defined as `method(HttpMethod.GET).and(path("/x"))`. Knowing this explains why order and composition matter. **When to use.** The functional style shines for lightweight services, clear route composition, and when you want routing logic to be first-class values you can compose/test. Annotations remain fine for large controllers. **Gotcha.** Because the first match wins, a broad predicate declared early can shadow a more specific one declared later.

  • What is a HandlerFunction's exact signature?
    It's a functional interface: Mono<ServerResponse> handle(ServerRequest request) — takes a ServerRequest, returns a Mono<ServerResponse>.
  • What does GET("/path") decompose into?
    method(HttpMethod.GET).and(path("/path")) — a method predicate AND-composed with a path predicate.

saying these in an interview costs you the question

  • Thinking you still need @GetMapping inside a functional endpoint
  • Believing all matching routes run, not just the first
  • Confusing HandlerFunction with @Controller handler methods

context