skip to content

HandlerFunction & ServerResponse

A HandlerFunction takes a ServerRequest and returns a Mono<ServerResponse>, extracting the body with bodyToMono or bodyToFlux and building the response fluently. Interviewers use it to check you can write reactive endpoints without annotations.

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

questions

5

What is a HandlerFunction<ServerResponse> in Spring WebFlux, and how does it differ from an annotated @RestController method?

level: juniorimportance: must knowfreq 60%

answer

  1. handle(ServerRequest) -> Mono<ServerResponse>
  2. functional model, not annotations
  3. wired via RouterFunction, not @GetMapping
  4. immutable ServerRequest/ServerResponse
  5. same runtime, different style

basics

~10 s

A HandlerFunction is a function that takes a ServerRequest and returns a Mono<ServerResponse>. It's the functional way to write a WebFlux endpoint, instead of using annotations like @GetMapping on a controller method.

solid answer

~40 s

HandlerFunction<ServerResponse> is a functional interface with one method: Mono<ServerResponse> handle(ServerRequest request). It represents a single reactive endpoint as a plain function rather than an annotated method. Instead of @RestController + @GetMapping, you write a handler that reads from ServerRequest and returns a Mono<ServerResponse>, then map it to a path via a RouterFunction. Both run on the same reactive engine and non-blocking runtime; the difference is style. The functional model keeps routing explicit and testable (handlers are just methods you can call directly), avoids annotation/reflection magic, and gives programmatic control over route composition. Annotated controllers are more declarative and familiar. Semantics — non-blocking I/O, Reactor types, the same HTTP contract — are identical.

code

java · 12 lines
java
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

// A HandlerFunction is just: ServerRequest -> Mono<ServerResponse>
public Mono<ServerResponse> hello(ServerRequest request) {
    return ServerResponse.ok()
            .bodyValue("Hello, " + request.pathVariable("name"));
}

// It can also be written as a lambda typed as HandlerFunction<ServerResponse>:
// HandlerFunction<ServerResponse> h = req -> ServerResponse.ok().bodyValue("hi");

go deeper

for a junior

Know the signature: ServerRequest in, Mono<ServerResponse> out, and that it's the annotation-free alternative to @RestController.

for a middle

Explain the immutability of ServerRequest/ServerResponse and that routing is explicit via RouterFunction.

for a senior

Discuss testability, composition, and that behavior/runtime is identical to annotated controllers — only the authoring style differs.

for a principal

Weigh when a functional style pays off (libraries, programmatic/conditional routing, filters as functions) vs. annotated declarativeness for large teams.

**Context.** Spring WebFlux is the reactive, non-blocking web stack (an alternative to Spring MVC). It offers two programming models: the *annotated* model (`@RestController`, `@GetMapping`, familiar from MVC) and the *functional* model (router + handler functions). This question is about the functional model's core building block. **HandlerFunction.** `HandlerFunction<T extends ServerResponse>` is a `@FunctionalInterface` in `org.springframework.web.reactive.function.server`. Its single method is: ```java Mono<T> handle(ServerRequest request); ``` So a handler receives one `ServerRequest` and returns a `Mono<ServerResponse>` (a reactive publisher that will emit exactly one response, asynchronously). Because it is a functional interface, a handler can be a lambda, a method reference, or a method on a `@Component` bean. **ServerRequest / ServerResponse.** These are *immutable*, WebFlux-specific abstractions (not the Servlet `HttpServletRequest`/`Response`). `ServerRequest` gives access to headers, path/query params, and the body via `bodyToMono`/`bodyToFlux`. `ServerResponse` is built with a fluent builder — `ServerResponse.ok().bodyValue(x)` — and the terminal builder call returns a `Mono<ServerResponse>`. **How it differs from an annotated controller.** - *Wiring*: annotated methods are discovered by reflection over `@RequestMapping` annotations; handler functions are wired explicitly through a `RouterFunction` bean (`RouterFunctions.route().GET("/x", handler::method).build()`). - *Testability*: a handler is a normal method taking a `ServerRequest` — you can construct a `MockServerRequest` and call it directly, no dispatcher needed. - *Composition*: routes are composed programmatically (`.and(...)`, nested routes, filters as ordinary functions), which is powerful for building libraries or conditional routing. - *No behavioral difference*: threading, non-blocking I/O, backpressure, content negotiation, and the resulting HTTP semantics are the same; it is purely a different authoring style. **When to use.** Functional endpoints shine when you want explicit, centralized routing, lightweight endpoints, or fine-grained programmatic control; annotated controllers are usually chosen for familiarity and large declarative APIs. The two can coexist in one application. **Gotchas.** - The return type is `Mono<ServerResponse>` — a *single* response publisher — even when the body itself is a `Flux` of many items (the body is a stream inside one response). - Handlers are typically registered as beans and referenced by method reference; they are not auto-scanned as endpoints the way `@GetMapping` methods are. - This is *reactive* WebFlux (`org.springframework.web.reactive.function.server`). There is a separate `org.springframework.web.servlet.function` variant for MVC that uses the same names but blocking types — don't mix imports.

  • Can annotated controllers and functional endpoints coexist in the same WebFlux app?
    Yes. Both models run on the same reactive infrastructure and can be used side by side; the DispatcherHandler routes to whichever matches. Teams often mix them, though most keep one style per module for consistency.
  • Why does handle() return a Mono<ServerResponse> and not just a ServerResponse?
    Because building the response may itself require async work (e.g. reading the request body, a DB call). Returning a Mono lets the whole pipeline stay non-blocking; the framework subscribes and writes the response when it resolves.

saying these in an interview costs you the question

  • Thinking HandlerFunction returns ServerResponse directly instead of Mono<ServerResponse>
  • Believing functional endpoints are blocking or non-reactive
  • Confusing WebFlux ServerRequest with the Servlet HttpServletRequest
  • Assuming handler methods are auto-discovered like @GetMapping (they must be wired via a RouterFunction)

context

open as a page

How do you extract the request body inside a HandlerFunction, and when do you use bodyToMono versus bodyToFlux?

level: middleimportance: must knowfreq 55%

basics

~10 s

Call request.bodyToMono(MyType.class) when the body is a single object, or request.bodyToFlux(MyType.class) when the body is a stream/array of many objects. Both return reactive publishers you then chain onto.

open as a page

How do you build a ServerResponse, and what is the difference between .body(...) and .bodyValue(...)?

level: middleimportance: must knowfreq 50%

basics

~10 s

Use the builder: ServerResponse.ok() (or .status(...), .created(uri), etc.), set headers, then a terminal call. Use bodyValue(obj) for a plain already-available object; use body(publisher, Class) when the body is a Mono/Flux.

open as a page

How do you wire handler beans into routes with a RouterFunction, and how does the request reach the right handler method?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Define a @Bean RouterFunction<ServerResponse> using RouterFunctions.route(), mapping each path+method predicate to a handler method reference (e.g. .GET("/users/{id}", handler::getUser)). Spring uses the router to dispatch each request to the matching handler.

open as a page

In a functional WebFlux endpoint, how do you handle errors and empty results so the client gets the right status, and what threading pitfalls must you avoid?

level: principalimportance: should knowfreq 35%

basics

~10 s

Keep everything in the reactive chain: use switchIfEmpty to turn an empty Mono into a 404/400 response, and onErrorResume to map exceptions to error responses. Never block; always return the composed Mono<ServerResponse>.

open as a page