skip to content

Functional Endpoints (WebMvc.fn)

WebMvc.fn declares routes as data — predicates mapped to handler functions — instead of scanning annotations, with nesting and filters. Interviewers ask when they want you to compare explicit routing against the annotation model.

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

questions

5

What is the functional endpoint model (WebMvc.fn) in Spring MVC, and what are its core building blocks?

level: juniorimportance: should knowfreq 35%

answer

  1. RouterFunction = routing table
  2. RequestPredicate -> HandlerFunction
  3. ServerRequest in, ServerResponse out
  4. Expose as @Bean, RouterFunctionMapping dispatches
  5. Spring 5.2, same Servlet stack

basics

~10 s

WebMvc.fn is an alternative to @Controller where you define routes in code. A RouterFunction maps a RequestPredicate (like GET /hello) to a HandlerFunction that takes a ServerRequest and returns a ServerResponse.

solid answer

~40 s

WebMvc.fn is Spring MVC's functional programming model (added in Spring 5.2), an alternative to annotation-based @Controller. Instead of annotations, you declare routing explicitly with a RouterFunction<ServerResponse>, built via the RouterFunctions.route() builder. Each route pairs a RequestPredicate (the matching condition, e.g. GET("/users/{id}")) with a HandlerFunction<ServerResponse> — a lambda that receives a ServerRequest and returns a ServerResponse. You expose the RouterFunction as a @Bean; Spring's RouterFunctionMapping discovers it and dispatches matching requests. Routing is data, not metadata: it's expressed as ordinary code you can compose, nest, and test. It still runs on the same blocking Servlet stack and uses the same HttpMessageConverters as annotated controllers.

code

java · 16 lines
java
import static org.springframework.web.servlet.function.RequestPredicates.accept;
import org.springframework.web.servlet.function.*;
import org.springframework.http.MediaType;
import org.springframework.context.annotation.*;

@Configuration
public class RoutingConfig {

    @Bean
    RouterFunction<ServerResponse> greetingRoutes() {
        return RouterFunctions.route()
            .GET("/hello", accept(MediaType.TEXT_PLAIN),
                 request -> ServerResponse.ok().body("Hello, WebMvc.fn"))
            .build();
    }
}

go deeper

for a junior

Know the three nouns: RouterFunction, RequestPredicate, HandlerFunction, and that ServerRequest goes in and ServerResponse comes out.

for a middle

Know it's declared as a @Bean, discovered by RouterFunctionMapping, and that first-match-wins ordering applies.

for a senior

Articulate that it shares the Servlet stack and message converters with annotated controllers, and the tradeoffs vs annotations.

for a principal

Frame it as routing-as-code enabling composition/generation, and reason about when centralized functional routing improves a codebase over scattered annotations.

## What it is **WebMvc.fn** is the *functional* web programming model for Spring MVC (the blocking, Servlet-based stack), introduced in **Spring Framework 5.2**. It is a lightweight alternative to the annotation model (`@RestController` / `@RequestMapping` / `@GetMapping`). The reactive stack (Spring WebFlux) has an analogous model called **WebFlux.fn**; the two share almost identical APIs but live in different packages (`org.springframework.web.servlet.function` vs `org.springframework.web.reactive.function.server`). ## The building blocks - **`RouterFunction<ServerResponse>`** — the routing table. Given a `ServerRequest`, it returns an `Optional<HandlerFunction<ServerResponse>>`: the handler to run, or empty if this router doesn't match. You almost never implement it by hand; you build it with the fluent `RouterFunctions.route()` builder. - **`RequestPredicate`** — the *condition* for a route (HTTP method, path pattern, content type, accept header, params, headers). Factory methods live in `RequestPredicates` (e.g. `GET`, `POST`, `path`, `accept`, `contentType`, `queryParam`). Predicates compose with `.and(...)` and `.or(...)`. - **`HandlerFunction<ServerResponse>`** — a functional interface: `ServerResponse handle(ServerRequest request) throws Exception`. This is your endpoint logic. Because it can throw `Exception`, handlers can propagate errors normally. - **`ServerRequest`** — an immutable view over the incoming request: path variables (`pathVariable`), query params (`param`), headers, and the body (`body(Class)`), read via the same `HttpMessageConverter`s as annotated controllers. - **`ServerResponse`** — a builder-style, immutable response: `ServerResponse.ok().body(obj)`, `ServerResponse.status(...)`, `ServerResponse.created(uri).build()`, etc. The body is serialized by message converters. ## How a request flows 1. You declare a `@Bean` that returns a `RouterFunction<ServerResponse>`. 2. Spring's **`RouterFunctionMapping`** collects all such beans at startup and combines them. 3. On each request, the `DispatcherServlet` consults handler mappings; `RouterFunctionMapping` asks each router whether it matches. The **first** matching route wins (order matters — routes are evaluated top to bottom). 4. The matched `HandlerFunction` runs and returns a `ServerResponse`, which is written to the client. ## Minimal example ```java @Bean RouterFunction<ServerResponse> routes(GreetingHandler h) { return RouterFunctions.route() .GET("/hello", accept(MediaType.TEXT_PLAIN), h::hello) .build(); } ``` ## When to use it - You want routing to be **explicit and centralized** rather than scattered across annotated classes. - You want to **compose or generate** routes programmatically (e.g. build many similar endpoints in a loop). - You prefer immutable request/response objects and lambda handlers. ## Gotchas - It runs on the **same blocking Servlet stack** — it is NOT reactive and does not make anything non-blocking. - **Order matters**: the first matching predicate wins, so put specific routes before general ones. - You lose some annotation conveniences (e.g. `@Valid` auto-triggering, `@ExceptionHandler` semantics differ) — validation and error handling become explicit. - Functional and annotated endpoints **coexist** in the same app; you don't have to choose one exclusively.

  • Does using WebMvc.fn make my endpoints non-blocking or reactive?
    No. WebMvc.fn runs on the same blocking Servlet stack as annotated controllers. It only changes how routing is declared. The reactive equivalent is WebFlux.fn on the WebFlux stack.
  • How does Spring find your RouterFunction?
    You expose it as a @Bean returning RouterFunction<ServerResponse>. At startup RouterFunctionMapping collects all such beans and combines them; DispatcherServlet dispatches matching requests to them.

saying these in an interview costs you the question

  • Claiming WebMvc.fn is reactive or non-blocking
  • Thinking functional endpoints replace and cannot coexist with @Controller
  • Confusing WebMvc.fn (servlet, org.springframework.web.servlet.function) with WebFlux.fn (reactive)

context

open as a page

How do you read request data with ServerRequest and build responses with ServerResponse in a functional handler?

level: middleimportance: should knowfreq 30%

basics

~10 s

ServerRequest gives you the body via request.body(Type.class), path variables via pathVariable("id"), and query params via param("q"). ServerResponse is a builder: ServerResponse.ok().body(obj) or ServerResponse.created(uri).build() to set status, headers, and body.

open as a page

How do nested routes (nest / path) and filter functions (filter / before / after / onError) work in WebMvc.fn?

level: seniorimportance: should knowfreq 28%

basics

~20 s

nest() (or path()) groups routes under a shared predicate like /users, so you don't repeat it. Inside a nest you can attach filters that apply only to those routes: filter() wraps the handler, before()/after() transform the request/response, and onError() maps exceptions to responses.

open as a page

How are functional endpoints registered and dispatched, and how do they coexist with annotated @Controllers in the same application?

level: seniorimportance: should knowfreq 22%

basics

~20 s

You expose RouterFunction<ServerResponse> beans. Spring's RouterFunctionMapping combines them and the DispatcherServlet dispatches matching requests to them. Annotated controllers use RequestMappingHandlerMapping. Both work together; by default the annotated handler mapping is consulted before the functional one.

open as a page

When would you choose functional endpoints over annotated controllers, what are the tradeoffs, and how do you test a RouterFunction?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Use functional endpoints when you want routing centralized, composable, or generated in code, and prefer explicit request/response handling. Annotations are more concise and feature-rich for typical CRUD. Test a RouterFunction by calling handlers directly with a mock ServerRequest, or via MockMvc against the context.

open as a page