In Spring WebFlux, what is the WebSocketHandler interface and what does its handle(WebSocketSession) method return, and why?
answer
- One method: Mono<Void> handle(WebSocketSession)
- Framework subscribes; complete closes the session
- Compose receive() + send(), return the Mono
- Mono<Void> = completion/error only, no value
- Don't call .subscribe() yourself
basics
~10 sWebSocketHandler is the reactive interface you implement to handle a WebSocket connection. Its single method handle(WebSocketSession) returns Mono<Void>, a reactive signal that completes when your handling of the session is done (which closes it).
solid answer
~40 sIn WebFlux, org.springframework.web.reactive.socket.WebSocketHandler has one method: Mono<Void> handle(WebSocketSession session). It is the reactive analog of the Servlet-stack WebSocketHandler. You don't return data; instead you build a reactive pipeline from the session — reading via session.receive() and writing via session.send(...) — and return a Mono<Void> that represents completion of that pipeline. The framework subscribes to that Mono. Because it is Mono<Void>, it carries no value, only completion or error signals: when it completes, the framework closes the session; if it errors, the session is closed with an error status. So the whole connection lifecycle is expressed as one reactive stream you compose and hand back. A trivial echo handler returns session.send(session.receive().map(...)).
code
java · 18 linesimport org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketSession;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
public class EchoHandler implements WebSocketHandler {
@Override
public Mono<Void> handle(WebSocketSession session) {
// Read inbound frames, map each to an outbound text frame, write them back.
// The returned Mono<Void> completes when the client stops sending;
// completion of that Mono tells the framework to close the session.
return session.send(
session.receive()
.map(msg -> session.textMessage("echo: " + msg.getPayloadAsText())));
}
}go deeper
Know it's one method returning Mono<Void> and that completing it closes the session.
Explain that the framework subscribes and that you compose receive()/send() into the returned Mono.
Contrast with the Servlet callback model and explain error signals closing with a close status.
Discuss lifecycle ownership, why callbacks were replaced by a single stream, and cleanup via doFinally/doOnError.
## The interface `org.springframework.web.reactive.socket.WebSocketHandler` is the reactive (non-blocking) contract for handling a WebSocket connection in Spring WebFlux. It is deliberately tiny: ```java public interface WebSocketHandler { Mono<Void> handle(WebSocketSession session); default List<String> getSubProtocols() { return Collections.emptyList(); } } ``` This is the reactive counterpart to the Servlet-stack `org.springframework.web.socket.WebSocketHandler`, but the programming model is completely different: instead of callback methods (`afterConnectionEstablished`, `handleMessage`, `afterConnectionClosed`), you get **one method** and express everything as reactive streams. ## Why Mono<Void>? `Mono<Void>` is a reactive publisher that emits **no value** — it only ever signals *completion* or *error*. It is Spring's idiom for "an asynchronous task that finishes but returns nothing." In `handle`, the returned `Mono<Void>` represents **"my handling of this session is finished."** Key consequences: - The framework (specifically `WebSocketHandlerAdapter` / the underlying `WebSocketService`) **subscribes** to the Mono you return. You must not subscribe yourself. - **When the Mono completes** → the framework closes the session with a normal close status. - **When the Mono errors** → the session is closed (with an error/close status). - **While the Mono is not yet terminated** → the session stays open. So the lifetime of the connection is tied to the lifetime of the stream you return. ## How you actually build it You don't "return messages." You compose the inbound and outbound streams and return a Mono that ties them together: - `session.receive()` → `Flux<WebSocketMessage>` of inbound frames. - `session.send(Publisher<WebSocketMessage>)` → `Mono<Void>` that completes when the outbound stream you passed completes. An echo handler: ```java public Mono<Void> handle(WebSocketSession session) { return session.send( session.receive().map(msg -> session.textMessage(msg.getPayloadAsText()))); } ``` Here `send(...)` returns a `Mono<Void>` that stays open as long as inbound messages keep arriving; when the client closes, `receive()` completes, `send()` completes, and the framework closes the session. ## Registration A `WebSocketHandler` is not picked up by URL by default. You map it to a path with a `SimpleUrlHandlerMapping` bean and enable the upgrade with a `WebSocketHandlerAdapter` bean (which internally uses `HandshakeWebSocketService`). That is covered in the mapping question. ## Gotchas for a beginner - **Don't call `.subscribe()`** on your pipeline — return it. If you subscribe yourself and return `Mono.empty()`, the framework will close the session immediately. - **Returning `Mono.empty()`** (or a completed Mono) closes the connection right away — useful only for reject/close-fast logic. - **This is not `@MessageMapping`.** `@MessageMapping` is STOMP/RSocket messaging (a different leaf). The low-level `WebSocketHandler` gives you raw frames. - **One method, whole lifecycle** — there is no separate close callback; use reactive operators like `doOnError`, `doFinally` on the stream for cleanup.
- What happens if handle() returns Mono.empty()?The framework subscribes, sees immediate completion, and closes the session right away. It is only useful to fast-close a connection, not to keep it open.
- Should you call subscribe() on your receive()/send() pipeline inside handle()?No. You return the Mono and the framework subscribes to it. Subscribing yourself detaches the stream from the session lifecycle and typically closes the connection prematurely.
saying these in an interview costs you the question
- Thinking WebSocketHandler has multiple callbacks like the Servlet version (afterConnectionEstablished, handleMessage)
- Calling .subscribe() inside handle() instead of returning the Mono
- Believing you must return the messages from handle() as a value
- Confusing raw WebSocketHandler with @MessageMapping/STOMP