0
# Functional Web Programming
1
2
Spring WebFlux provides a functional programming model using RouterFunction and HandlerFunction for composable request handling without annotations. This approach offers fine-grained control over request routing and processing with a declarative, functional style.
3
4
## Capabilities
5
6
### Router Functions
7
8
Core interface for routing requests to handler functions in the functional programming model.
9
10
```java { .api }
11
@FunctionalInterface
12
interface RouterFunction<T extends ServerResponse> {
13
/**
14
* Route the given request to a handler function.
15
* @param request the request to route
16
* @return an optional handler function
17
*/
18
Mono<HandlerFunction<T>> route(ServerRequest request);
19
20
// Composition methods
21
RouterFunction<T> and(RouterFunction<T> other);
22
RouterFunction<?> andOther(RouterFunction<?> other);
23
RouterFunction<T> andRoute(RequestPredicate predicate, HandlerFunction<T> handlerFunction);
24
RouterFunction<T> andNest(RequestPredicate predicate, RouterFunction<T> routerFunction);
25
}
26
```
27
28
**Usage Examples:**
29
30
```java
31
import static org.springframework.web.reactive.function.server.RouterFunctions.*;
32
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
33
34
// Simple route
35
RouterFunction<ServerResponse> helloRoute = route(
36
GET("/hello"),
37
request -> ServerResponse.ok().bodyValue("Hello World!")
38
);
39
40
// Combined routes
41
RouterFunction<ServerResponse> userRoutes = route()
42
.GET("/users", this::getAllUsers)
43
.GET("/users/{id}", this::getUser)
44
.POST("/users", this::createUser)
45
.PUT("/users/{id}", this::updateUser)
46
.DELETE("/users/{id}", this::deleteUser)
47
.build();
48
```
49
50
### RouterFunctions Factory
51
52
Factory class providing static methods for creating router functions and serving resources.
53
54
```java { .api }
55
class RouterFunctions {
56
// Basic routing
57
static <T extends ServerResponse> RouterFunction<T> route(
58
RequestPredicate predicate,
59
HandlerFunction<T> handlerFunction
60
);
61
62
// Nested routing
63
static <T extends ServerResponse> RouterFunction<T> nest(
64
RequestPredicate predicate,
65
RouterFunction<T> routerFunction
66
);
67
68
// Static resources
69
static RouterFunction<?> resources(String pattern, Resource location);
70
static RouterFunction<?> resources(Function<ServerRequest, Mono<Resource>> lookupFunction);
71
}
72
```
73
74
**Usage Examples:**
75
76
```java
77
// Nested routes with path prefix
78
RouterFunction<ServerResponse> apiRoutes = nest(
79
path("/api/v1"),
80
route()
81
.GET("/users", userHandler::getAllUsers)
82
.POST("/users", userHandler::createUser)
83
.nest(path("/users/{id}"),
84
route()
85
.GET("", userHandler::getUser)
86
.PUT("", userHandler::updateUser)
87
.DELETE("", userHandler::deleteUser)
88
.build()
89
)
90
.build()
91
);
92
93
// Static resource serving
94
RouterFunction<?> staticResources = resources("/static/**",
95
new ClassPathResource("static/"));
96
```
97
98
### Handler Functions
99
100
Functional interface for handling HTTP requests and producing responses.
101
102
```java { .api }
103
@FunctionalInterface
104
interface HandlerFunction<T extends ServerResponse> {
105
/**
106
* Handle the given request.
107
* @param request the request to handle
108
* @return the response
109
*/
110
Mono<T> handle(ServerRequest request);
111
}
112
```
113
114
**Usage Examples:**
115
116
```java
117
// Simple handler
118
HandlerFunction<ServerResponse> helloHandler = request ->
119
ServerResponse.ok().bodyValue("Hello, " + request.pathVariable("name"));
120
121
// Handler with request body
122
HandlerFunction<ServerResponse> createUserHandler = request ->
123
request.bodyToMono(User.class)
124
.flatMap(userService::save)
125
.flatMap(user -> ServerResponse.ok().bodyValue(user))
126
.onErrorResume(ex -> ServerResponse.badRequest().bodyValue("Invalid user data"));
127
128
// Handler class
129
@Component
130
public class UserHandler {
131
132
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
133
return userService.findAll()
134
.collectList()
135
.flatMap(users -> ServerResponse.ok().bodyValue(users));
136
}
137
138
public Mono<ServerResponse> getUser(ServerRequest request) {
139
String id = request.pathVariable("id");
140
return userService.findById(id)
141
.flatMap(user -> ServerResponse.ok().bodyValue(user))
142
.switchIfEmpty(ServerResponse.notFound().build());
143
}
144
}
145
```
146
147
### Request Predicates
148
149
Interface for matching HTTP requests in functional routing with logical operations.
150
151
```java { .api }
152
@FunctionalInterface
153
interface RequestPredicate {
154
/**
155
* Evaluate this predicate on the given request.
156
* @param request the request to match against
157
* @return true if the request matches the predicate
158
*/
159
boolean test(ServerRequest request);
160
161
/**
162
* Return a composed predicate based on a nest operation.
163
* @param request the request to nest
164
* @return an optional nested request
165
*/
166
Optional<ServerRequest> nest(ServerRequest request);
167
168
// Logical operations
169
RequestPredicate and(RequestPredicate other);
170
RequestPredicate negate();
171
RequestPredicate or(RequestPredicate other);
172
}
173
```
174
175
### RequestPredicates Factory
176
177
Factory class providing static methods for creating request predicates.
178
179
```java { .api }
180
class RequestPredicates {
181
// Basic predicates
182
static RequestPredicate all();
183
static RequestPredicate method(HttpMethod httpMethod);
184
static RequestPredicate path(String pattern);
185
static RequestPredicate pathExtension(String extension);
186
static RequestPredicate pathExtension(Predicate<String> extensionPredicate);
187
188
// HTTP method predicates
189
static RequestPredicate GET(String pattern);
190
static RequestPredicate POST(String pattern);
191
static RequestPredicate PUT(String pattern);
192
static RequestPredicate DELETE(String pattern);
193
static RequestPredicate PATCH(String pattern);
194
static RequestPredicate HEAD(String pattern);
195
static RequestPredicate OPTIONS(String pattern);
196
197
// Parameter and header predicates
198
static RequestPredicate queryParam(String name, String value);
199
static RequestPredicate queryParam(String name, Predicate<String> predicate);
200
static RequestPredicate header(String name, String value);
201
static RequestPredicate header(String name, Predicate<String> predicate);
202
203
// Content negotiation predicates
204
static RequestPredicate contentType(MediaType... mediaTypes);
205
static RequestPredicate accept(MediaType... mediaTypes);
206
}
207
```
208
209
**Usage Examples:**
210
211
```java
212
// HTTP method predicates
213
RequestPredicate getUsersPredicate = GET("/users");
214
RequestPredicate createUserPredicate = POST("/users");
215
216
// Combined predicates
217
RequestPredicate jsonApiPredicate = GET("/api/**")
218
.and(accept(MediaType.APPLICATION_JSON))
219
.and(header("X-API-Version", "v1"));
220
221
// Query parameter predicates
222
RequestPredicate searchPredicate = GET("/search")
223
.and(queryParam("q", q -> !q.isEmpty()));
224
225
// Path extension predicates
226
RequestPredicate imagesPredicate = path("/images/**")
227
.and(pathExtension(ext -> ext.matches("jpg|png|gif")));
228
```
229
230
### Server Request
231
232
Interface representing server-side HTTP request in the functional model with comprehensive access to request data.
233
234
```java { .api }
235
interface ServerRequest {
236
// Basic request information
237
HttpMethod method();
238
URI uri();
239
UriBuilder uriBuilder();
240
String path();
241
RequestPath requestPath();
242
243
// Headers and cookies
244
Headers headers();
245
MultiValueMap<String, HttpCookie> cookies();
246
247
// Network information
248
Optional<InetSocketAddress> remoteAddress();
249
Optional<InetSocketAddress> localAddress();
250
251
// Body extraction
252
List<HttpMessageReader<?>> messageReaders();
253
<T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor);
254
<T> Mono<T> bodyToMono(Class<? extends T> elementClass);
255
<T> Mono<T> bodyToMono(ParameterizedTypeReference<T> elementTypeRef);
256
<T> Flux<T> bodyToFlux(Class<? extends T> elementClass);
257
<T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef);
258
259
// Path variables and query parameters
260
Optional<String> pathVariable(String name);
261
Map<String, String> pathVariables();
262
Optional<String> queryParam(String name);
263
MultiValueMap<String, String> queryParams();
264
265
// Session and security
266
Mono<WebSession> session();
267
Mono<? extends Principal> principal();
268
269
// Data binding
270
Mono<ServerRequest> bind(Object bean);
271
272
// Access to underlying exchange
273
ServerWebExchange exchange();
274
}
275
```
276
277
**Usage Examples:**
278
279
```java
280
// Extract path variables
281
HandlerFunction<ServerResponse> getUserHandler = request -> {
282
String userId = request.pathVariable("id");
283
return userService.findById(userId)
284
.flatMap(user -> ServerResponse.ok().bodyValue(user))
285
.switchIfEmpty(ServerResponse.notFound().build());
286
};
287
288
// Extract query parameters
289
HandlerFunction<ServerResponse> searchHandler = request -> {
290
String query = request.queryParam("q").orElse("");
291
int page = request.queryParam("page")
292
.map(Integer::parseInt)
293
.orElse(0);
294
295
return searchService.search(query, page)
296
.flatMap(results -> ServerResponse.ok().bodyValue(results));
297
};
298
299
// Extract request body
300
HandlerFunction<ServerResponse> createUserHandler = request ->
301
request.bodyToMono(User.class)
302
.flatMap(user -> {
303
// Validate user
304
if (user.getName() == null || user.getName().isEmpty()) {
305
return ServerResponse.badRequest()
306
.bodyValue("Name is required");
307
}
308
return userService.save(user)
309
.flatMap(savedUser -> ServerResponse
310
.created(URI.create("/users/" + savedUser.getId()))
311
.bodyValue(savedUser));
312
});
313
```
314
315
### Server Response
316
317
Interface representing server-side HTTP response with static factory methods for common response types.
318
319
```java { .api }
320
interface ServerResponse {
321
// Response information
322
HttpStatusCode statusCode();
323
HttpHeaders headers();
324
MultiValueMap<String, ResponseCookie> cookies();
325
326
// Write response
327
Mono<Void> writeTo(ServerWebExchange exchange, Context context);
328
329
// Static factory methods - Status codes
330
static BodyBuilder from(ServerResponse other);
331
static Mono<ServerResponse> from(ErrorResponse response);
332
static BodyBuilder status(HttpStatusCode status);
333
static BodyBuilder status(int status);
334
335
// 2xx Success
336
static BodyBuilder ok();
337
static BodyBuilder created(URI location);
338
static BodyBuilder accepted();
339
static BodyBuilder noContent();
340
341
// 3xx Redirection
342
static BodyBuilder seeOther(URI location);
343
static BodyBuilder notModified();
344
static BodyBuilder temporaryRedirect(URI location);
345
static BodyBuilder permanentRedirect(URI location);
346
347
// 4xx Client Error
348
static BodyBuilder badRequest();
349
static BodyBuilder notFound();
350
static BodyBuilder unprocessableEntity();
351
}
352
```
353
354
**Usage Examples:**
355
356
```java
357
// Simple responses
358
return ServerResponse.ok().bodyValue("Success");
359
return ServerResponse.notFound().build();
360
return ServerResponse.created(URI.create("/users/123")).bodyValue(user);
361
362
// Response with headers
363
return ServerResponse.ok()
364
.header("X-Custom-Header", "value")
365
.contentType(MediaType.APPLICATION_JSON)
366
.bodyValue(data);
367
368
// Response with cookies
369
return ServerResponse.ok()
370
.cookie(ResponseCookie.from("session", sessionId)
371
.httpOnly(true)
372
.secure(true)
373
.build())
374
.bodyValue("Authenticated");
375
```
376
377
### Server Response Body Builder
378
379
Interface for building responses with body content and comprehensive header/cookie support.
380
381
```java { .api }
382
interface BodyBuilder extends ServerResponse.HeadersBuilder<BodyBuilder> {
383
// Headers
384
BodyBuilder header(String headerName, String... headerValues);
385
BodyBuilder headers(Consumer<HttpHeaders> headersConsumer);
386
BodyBuilder cookie(ResponseCookie cookie);
387
BodyBuilder cookies(Consumer<MultiValueMap<String, ResponseCookie>> cookiesConsumer);
388
389
// HTTP headers
390
BodyBuilder allow(HttpMethod... allowedMethods);
391
BodyBuilder allow(Set<HttpMethod> allowedMethods);
392
BodyBuilder contentType(MediaType contentType);
393
BodyBuilder contentLength(long contentLength);
394
BodyBuilder lastModified(ZonedDateTime lastModified);
395
BodyBuilder lastModified(Instant lastModified);
396
BodyBuilder location(URI location);
397
BodyBuilder cacheControl(CacheControl cacheControl);
398
BodyBuilder varyBy(String... requestHeaders);
399
400
// Build response
401
Mono<ServerResponse> build();
402
Mono<ServerResponse> build(Publisher<Void> voidPublisher);
403
404
// Body methods
405
Mono<ServerResponse> bodyValue(Object body);
406
<T, P extends Publisher<T>> Mono<ServerResponse> body(P publisher, Class<T> elementClass);
407
<T, P extends Publisher<T>> Mono<ServerResponse> body(P publisher, ParameterizedTypeReference<T> elementTypeRef);
408
Mono<ServerResponse> body(Object producer, Class<?> elementClass);
409
Mono<ServerResponse> body(Object producer, ParameterizedTypeReference<?> elementTypeRef);
410
Mono<ServerResponse> body(BodyInserter<?, ? super ServerHttpResponse> inserter);
411
412
// View rendering
413
Mono<ServerResponse> render(String name, Object... modelAttributes);
414
Mono<ServerResponse> render(String name, Map<String, ?> model);
415
}
416
```
417
418
**Usage Examples:**
419
420
```java
421
// JSON response with caching
422
return ServerResponse.ok()
423
.contentType(MediaType.APPLICATION_JSON)
424
.cacheControl(CacheControl.maxAge(Duration.ofMinutes(10)))
425
.bodyValue(data);
426
427
// Stream response
428
Flux<DataItem> dataStream = dataService.getDataStream();
429
return ServerResponse.ok()
430
.contentType(MediaType.APPLICATION_NDJSON)
431
.body(dataStream, DataItem.class);
432
433
// Template rendering
434
return ServerResponse.ok()
435
.render("user-profile", Map.of(
436
"user", user,
437
"posts", posts
438
));
439
```
440
441
### Specialized Responses
442
443
Specialized response types for strongly-typed entities and template rendering.
444
445
```java { .api }
446
interface EntityResponse<T> extends ServerResponse {
447
/**
448
* Return the entity that makes up this response.
449
*/
450
T entity();
451
452
// Static factory methods
453
static <T> Builder<T> fromObject(T t);
454
static <T> BodyBuilder fromPublisher(Publisher<T> publisher, Class<T> elementClass);
455
static <T> BodyBuilder fromPublisher(Publisher<T> publisher, ParameterizedTypeReference<T> elementTypeRef);
456
}
457
458
interface RenderingResponse extends ServerResponse {
459
/**
460
* Return the name of the template to be rendered.
461
*/
462
String name();
463
464
/**
465
* Return the unmodifiable model map.
466
*/
467
Map<String, Object> model();
468
469
// Static factory method
470
static Builder create(String name);
471
}
472
```
473
474
### Handler Filters
475
476
Interface for filtering handler function execution with pre/post processing.
477
478
```java { .api }
479
@FunctionalInterface
480
interface HandlerFilterFunction<T extends ServerResponse, R extends ServerResponse> {
481
/**
482
* Filter the given handler function.
483
* @param request the request
484
* @param next the next handler or filter in the chain
485
* @return the filtered response
486
*/
487
Mono<R> filter(ServerRequest request, HandlerFunction<T> next);
488
489
/**
490
* Apply this filter to the given handler function.
491
* @param handlerFunction the handler function to filter
492
* @return a filtered handler function
493
*/
494
default HandlerFunction<R> apply(HandlerFunction<T> handlerFunction) {
495
return request -> filter(request, handlerFunction);
496
}
497
}
498
```
499
500
**Usage Examples:**
501
502
```java
503
// Logging filter
504
HandlerFilterFunction<ServerResponse, ServerResponse> loggingFilter =
505
(request, next) -> {
506
logger.info("Processing request: {} {}", request.method(), request.path());
507
return next.handle(request)
508
.doOnSuccess(response ->
509
logger.info("Response status: {}", response.statusCode()));
510
};
511
512
// Authentication filter
513
HandlerFilterFunction<ServerResponse, ServerResponse> authFilter =
514
(request, next) -> {
515
return request.headers().firstHeader("Authorization")
516
.map(authService::validateToken)
517
.filter(Boolean::booleanValue)
518
.map(valid -> next.handle(request))
519
.orElse(ServerResponse.status(HttpStatus.UNAUTHORIZED).build());
520
};
521
522
// Apply filters to routes
523
RouterFunction<ServerResponse> protectedRoutes = route()
524
.GET("/admin/**", adminHandler::handleAdminRequest)
525
.filter(authFilter)
526
.filter(loggingFilter)
527
.build();
528
```
529
530
### Handler Strategies
531
532
Configuration interface for HTTP message readers, writers, and other strategies used by handler functions.
533
534
```java { .api }
535
interface HandlerStrategies {
536
// Strategy components
537
List<HttpMessageReader<?>> messageReaders();
538
List<HttpMessageWriter<?>> messageWriters();
539
List<ViewResolver> viewResolvers();
540
541
// Configuration
542
Builder mutate();
543
544
// Factory methods
545
static HandlerStrategies withDefaults();
546
static Builder builder();
547
static Builder empty();
548
}
549
```
550
551
### Resource Handling
552
553
Handler function for serving static resources with lookup capabilities.
554
555
```java { .api }
556
class ResourceHandlerFunction implements HandlerFunction<ServerResponse> {
557
/**
558
* Create a new ResourceHandlerFunction with the given Resource.
559
* @param resource the resource to serve
560
*/
561
ResourceHandlerFunction(Resource resource);
562
563
/**
564
* Create a new ResourceHandlerFunction with the given lookup function.
565
* @param lookupFunction function to look up the Resource
566
*/
567
ResourceHandlerFunction(Function<ServerRequest, Mono<Resource>> lookupFunction);
568
569
@Override
570
Mono<ServerResponse> handle(ServerRequest request);
571
}
572
```
573
574
**Usage Examples:**
575
576
```java
577
// Single resource handler
578
ResourceHandlerFunction faviconHandler =
579
new ResourceHandlerFunction(new ClassPathResource("favicon.ico"));
580
581
// Dynamic resource lookup
582
ResourceHandlerFunction dynamicHandler =
583
new ResourceHandlerFunction(request -> {
584
String path = request.path().replaceFirst("/static", "");
585
return Mono.just(new ClassPathResource("static" + path));
586
});
587
588
// Register as route
589
RouterFunction<ServerResponse> staticRoutes = route()
590
.GET("/favicon.ico", faviconHandler)
591
.GET("/static/**", dynamicHandler)
592
.build();
593
```