0
# Request Processing Pipeline
1
2
The Spring WebFlux request processing pipeline consists of core framework components that handle request dispatching, handler mapping, adaptation, and result processing in reactive web applications. This infrastructure provides the foundation for both annotation-based and functional programming models.
3
4
## Capabilities
5
6
### Dispatcher Handler
7
8
Central dispatcher for HTTP request handlers and controllers in reactive web applications, similar to Spring MVC's DispatcherServlet.
9
10
```java { .api }
11
class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware {
12
/**
13
* Create a new DispatcherHandler which needs to be configured with an ApplicationContext.
14
*/
15
DispatcherHandler();
16
17
/**
18
* Create a new DispatcherHandler for the given ApplicationContext.
19
* @param applicationContext the application context
20
*/
21
DispatcherHandler(ApplicationContext applicationContext);
22
23
/**
24
* Handle the web server exchange.
25
* @param exchange the current server exchange
26
* @return completion signal
27
*/
28
@Override
29
Mono<Void> handle(ServerWebExchange exchange);
30
31
/**
32
* Return the configured HandlerMapping beans.
33
* @return list of handler mappings
34
*/
35
List<HandlerMapping> getHandlerMappings();
36
37
/**
38
* Handle a CORS pre-flight request.
39
* @param exchange the current server exchange
40
* @return completion signal
41
*/
42
@Override
43
Mono<Void> handlePreFlight(ServerWebExchange exchange);
44
}
45
```
46
47
**Usage Examples:**
48
49
```java
50
// Configure DispatcherHandler as main WebHandler
51
@Configuration
52
public class WebFluxConfig {
53
54
@Bean
55
public DispatcherHandler dispatcherHandler(ApplicationContext context) {
56
return new DispatcherHandler(context);
57
}
58
59
// Or use auto-configuration with @EnableWebFlux
60
@Bean
61
public RouterFunction<ServerResponse> routes() {
62
return RouterFunctions.route()
63
.GET("/api/health", request -> ServerResponse.ok().bodyValue("OK"))
64
.build();
65
}
66
}
67
68
// Custom WebHandler that delegates to DispatcherHandler
69
@Component
70
public class CustomWebHandler implements WebHandler {
71
72
private final DispatcherHandler dispatcherHandler;
73
74
public CustomWebHandler(DispatcherHandler dispatcherHandler) {
75
this.dispatcherHandler = dispatcherHandler;
76
}
77
78
@Override
79
public Mono<Void> handle(ServerWebExchange exchange) {
80
// Pre-processing
81
exchange.getAttributes().put("startTime", System.currentTimeMillis());
82
83
return dispatcherHandler.handle(exchange)
84
.doFinally(signal -> {
85
long duration = System.currentTimeMillis() -
86
(Long) exchange.getAttributes().get("startTime");
87
logger.info("Request processed in {}ms", duration);
88
});
89
}
90
}
91
```
92
93
### Handler Mapping
94
95
Interface for mapping requests to handler objects with support for path variables and request attributes.
96
97
```java { .api }
98
interface HandlerMapping {
99
/**
100
* Return a handler for this request.
101
* @param exchange the current exchange
102
* @return a handler instance, or an empty Mono if none found
103
*/
104
Mono<Object> getHandler(ServerWebExchange exchange);
105
106
// Constants for request attributes
107
String BEST_MATCHING_HANDLER_ATTRIBUTE = "bestMatchingHandler";
108
String BEST_MATCHING_PATTERN_ATTRIBUTE = "bestMatchingPattern";
109
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = "pathWithinHandlerMapping";
110
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = "uriTemplateVariables";
111
String MATRIX_VARIABLES_ATTRIBUTE = "matrixVariables";
112
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = "producibleMediaTypes";
113
}
114
```
115
116
**Usage Examples:**
117
118
```java
119
// Custom handler mapping
120
@Component
121
public class CustomHandlerMapping implements HandlerMapping {
122
123
private final Map<String, Object> handlers = new HashMap<>();
124
125
public CustomHandlerMapping() {
126
handlers.put("/custom/endpoint", new CustomHandler());
127
}
128
129
@Override
130
public Mono<Object> getHandler(ServerWebExchange exchange) {
131
String path = exchange.getRequest().getPath().value();
132
Object handler = handlers.get(path);
133
134
if (handler != null) {
135
// Set handler mapping attributes
136
exchange.getAttributes().put(
137
BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
138
exchange.getAttributes().put(
139
BEST_MATCHING_PATTERN_ATTRIBUTE, path);
140
141
return Mono.just(handler);
142
}
143
144
return Mono.empty();
145
}
146
}
147
148
// Access handler mapping attributes in handler
149
@RestController
150
public class ExampleController {
151
152
@GetMapping("/users/{id}")
153
public Mono<User> getUser(@PathVariable String id, ServerWebExchange exchange) {
154
// Access URI template variables
155
Map<String, String> pathVars = exchange.getAttribute(
156
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
157
158
String userId = pathVars.get("id");
159
return userService.findById(userId);
160
}
161
}
162
```
163
164
### Handler Adapter
165
166
Interface for adapting handler invocation to a common interface, allowing different handler types to be processed uniformly.
167
168
```java { .api }
169
interface HandlerAdapter {
170
/**
171
* Check whether this adapter can handle the given handler instance.
172
* @param handler the handler object to check
173
* @return true if this adapter can adapt the given handler
174
*/
175
boolean supports(Object handler);
176
177
/**
178
* Handle the request with the given handler.
179
* @param exchange the current exchange
180
* @param handler the selected handler
181
* @return the handler result
182
*/
183
Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler);
184
}
185
```
186
187
**Usage Examples:**
188
189
```java
190
// Custom handler adapter for specific handler type
191
@Component
192
public class CustomHandlerAdapter implements HandlerAdapter {
193
194
@Override
195
public boolean supports(Object handler) {
196
return handler instanceof CustomHandler;
197
}
198
199
@Override
200
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
201
CustomHandler customHandler = (CustomHandler) handler;
202
203
return customHandler.process(exchange)
204
.map(result -> new HandlerResult(handler, result, null))
205
.doOnError(ex -> logger.error("Handler error", ex));
206
}
207
}
208
209
// Using built-in adapters
210
@Configuration
211
public class WebFluxConfig {
212
213
@Bean
214
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
215
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
216
adapter.setArgumentResolvers(customArgumentResolvers());
217
adapter.setInitBinderArgumentResolvers(initBinderArgumentResolvers());
218
return adapter;
219
}
220
221
@Bean
222
public HandlerFunctionAdapter handlerFunctionAdapter() {
223
return new HandlerFunctionAdapter();
224
}
225
}
226
```
227
228
### Handler Result
229
230
Class representing the result of handler invocation, containing the handler, return value, and binding context.
231
232
```java { .api }
233
class HandlerResult {
234
/**
235
* Create a new HandlerResult.
236
* @param handler the handler that handled the request
237
* @param returnValue the return value from the handler
238
* @param returnType the return type
239
*/
240
HandlerResult(Object handler, Object returnValue, MethodParameter returnType);
241
242
/**
243
* Create a new HandlerResult with binding context.
244
* @param handler the handler that handled the request
245
* @param returnValue the return value from the handler
246
* @param returnType the return type
247
* @param context the binding context
248
*/
249
HandlerResult(Object handler, Object returnValue, MethodParameter returnType, BindingContext context);
250
251
/**
252
* Return the handler that processed the request.
253
*/
254
Object getHandler();
255
256
/**
257
* Return the return value from the handler.
258
*/
259
Object getReturnValue();
260
261
/**
262
* Return the type of the return value.
263
*/
264
ResolvableType getReturnType();
265
266
/**
267
* Return the MethodParameter for the return type.
268
*/
269
MethodParameter getReturnTypeSource();
270
271
/**
272
* Return the BindingContext used for the request.
273
*/
274
BindingContext getBindingContext();
275
276
/**
277
* Return the model for the request.
278
*/
279
Model getModel();
280
281
/**
282
* Configure exception handling for this result.
283
* @param exceptionHandler the exception handler
284
* @return this HandlerResult for method chaining
285
*/
286
HandlerResult setExceptionHandler(DispatchExceptionHandler exceptionHandler);
287
}
288
```
289
290
**Usage Examples:**
291
292
```java
293
// Create handler result in custom adapter
294
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
295
return processHandler(handler, exchange)
296
.map(result -> {
297
MethodParameter returnType = getReturnType(handler);
298
BindingContext context = new BindingContext();
299
300
return new HandlerResult(handler, result, returnType, context);
301
});
302
}
303
304
// Access handler result in result handler
305
@Component
306
public class CustomResultHandler implements HandlerResultHandler {
307
308
@Override
309
public boolean supports(HandlerResult result) {
310
return result.getReturnValue() instanceof CustomResult;
311
}
312
313
@Override
314
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
315
CustomResult customResult = (CustomResult) result.getReturnValue();
316
Object handler = result.getHandler();
317
Model model = result.getModel();
318
319
// Process the custom result
320
return writeResponse(exchange, customResult, model);
321
}
322
}
323
```
324
325
### Handler Result Handler
326
327
Interface for processing handler results and writing HTTP responses.
328
329
```java { .api }
330
interface HandlerResultHandler {
331
/**
332
* Check whether this handler can handle the given HandlerResult.
333
* @param result the result object to check
334
* @return true if this handler can handle the result
335
*/
336
boolean supports(HandlerResult result);
337
338
/**
339
* Process the given result and handle the response.
340
* @param exchange the current exchange
341
* @param result the result to handle
342
* @return completion signal
343
*/
344
Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result);
345
}
346
```
347
348
**Usage Examples:**
349
350
```java
351
// Custom result handler for specific return types
352
@Component
353
public class JsonResponseHandler implements HandlerResultHandler {
354
355
private final ObjectMapper objectMapper;
356
357
public JsonResponseHandler(ObjectMapper objectMapper) {
358
this.objectMapper = objectMapper;
359
}
360
361
@Override
362
public boolean supports(HandlerResult result) {
363
Class<?> returnType = result.getReturnType().resolve();
364
return returnType != null &&
365
returnType.isAnnotationPresent(JsonResponse.class);
366
}
367
368
@Override
369
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
370
Object returnValue = result.getReturnValue();
371
372
return writeJsonResponse(exchange, returnValue)
373
.doOnError(ex -> logger.error("Failed to write JSON response", ex));
374
}
375
376
private Mono<Void> writeJsonResponse(ServerWebExchange exchange, Object value) {
377
try {
378
String json = objectMapper.writeValueAsString(value);
379
ServerHttpResponse response = exchange.getResponse();
380
381
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
382
DataBuffer buffer = response.bufferFactory().wrap(json.getBytes());
383
384
return response.writeWith(Mono.just(buffer));
385
} catch (Exception e) {
386
return Mono.error(e);
387
}
388
}
389
}
390
391
// Built-in result handlers configuration
392
@Configuration
393
public class ResultHandlerConfig {
394
395
@Bean
396
public ResponseEntityResultHandler responseEntityResultHandler() {
397
return new ResponseEntityResultHandler(List.of(new JsonMessageWriter()),
398
contentTypeResolver());
399
}
400
401
@Bean
402
public ResponseBodyResultHandler responseBodyResultHandler() {
403
return new ResponseBodyResultHandler(List.of(new JsonMessageWriter()),
404
contentTypeResolver());
405
}
406
407
@Bean
408
public ViewResolutionResultHandler viewResolutionResultHandler() {
409
return new ViewResolutionResultHandler(viewResolvers(),
410
contentTypeResolver());
411
}
412
}
413
```
414
415
### Binding Context
416
417
Context for data binding operations and shared model management across request processing.
418
419
```java { .api }
420
class BindingContext {
421
/**
422
* Create a new BindingContext.
423
*/
424
BindingContext();
425
426
/**
427
* Create a new BindingContext with a WebBindingInitializer.
428
* @param initializer the initializer to use
429
*/
430
BindingContext(WebBindingInitializer initializer);
431
432
/**
433
* Create a new BindingContext with initializer and adapter registry.
434
* @param initializer the initializer to use
435
* @param registry the reactive adapter registry
436
*/
437
BindingContext(WebBindingInitializer initializer, ReactiveAdapterRegistry registry);
438
439
/**
440
* Return the default model for the request.
441
*/
442
Model getModel();
443
444
/**
445
* Create a WebExchangeDataBinder for the target object.
446
* @param exchange the current exchange
447
* @param target the target object to bind to
448
* @param name the name of the target object
449
* @return the data binder
450
*/
451
WebExchangeDataBinder createDataBinder(ServerWebExchange exchange, Object target, String name);
452
453
/**
454
* Create a WebExchangeDataBinder without a target object.
455
* @param exchange the current exchange
456
* @param name the name for the binder
457
* @return the data binder
458
*/
459
WebExchangeDataBinder createDataBinder(ServerWebExchange exchange, String name);
460
461
/**
462
* Configure whether method validation is applicable.
463
* @param methodValidationApplicable true if method validation should be applied
464
*/
465
void setMethodValidationApplicable(boolean methodValidationApplicable);
466
467
/**
468
* Update the model before view rendering.
469
* @param exchange the current exchange
470
*/
471
void updateModel(ServerWebExchange exchange);
472
}
473
```
474
475
**Usage Examples:**
476
477
```java
478
// Using BindingContext in custom argument resolver
479
@Component
480
public class CustomModelAttributeResolver implements HandlerMethodArgumentResolver {
481
482
@Override
483
public boolean supportsParameter(MethodParameter parameter) {
484
return parameter.hasParameterAnnotation(ModelAttribute.class);
485
}
486
487
@Override
488
public Mono<Object> resolveArgument(MethodParameter parameter,
489
BindingContext bindingContext,
490
ServerWebExchange exchange) {
491
492
String name = getAttributeName(parameter);
493
Class<?> type = parameter.getParameterType();
494
495
// Create target object
496
Object target = BeanUtils.instantiateClass(type);
497
498
// Create data binder
499
WebExchangeDataBinder binder = bindingContext.createDataBinder(exchange, target, name);
500
501
// Bind request data
502
return binder.bind(exchange)
503
.doOnSuccess(result -> {
504
// Add to model
505
bindingContext.getModel().addAttribute(name, target);
506
})
507
.then(Mono.just(target));
508
}
509
}
510
511
// Using BindingContext in controller method
512
@RestController
513
public class UserController {
514
515
@PostMapping("/users")
516
public Mono<User> createUser(@RequestBody User user,
517
BindingContext bindingContext,
518
ServerWebExchange exchange) {
519
520
// Create validator
521
WebExchangeDataBinder binder = bindingContext.createDataBinder(exchange, user, "user");
522
523
return binder.bind(exchange)
524
.then(Mono.fromCallable(() -> {
525
BindingResult result = binder.getBindingResult();
526
if (result.hasErrors()) {
527
throw new ValidationException(result);
528
}
529
return user;
530
}))
531
.flatMap(userService::save);
532
}
533
}
534
```
535
536
### Dispatch Exception Handler
537
538
Interface for mapping exceptions to handler results, providing centralized exception handling.
539
540
```java { .api }
541
@FunctionalInterface
542
interface DispatchExceptionHandler {
543
/**
544
* Handle the given exception and return a HandlerResult.
545
* @param exchange the current exchange
546
* @param ex the exception to handle
547
* @return the handler result for the exception
548
*/
549
Mono<HandlerResult> handleError(ServerWebExchange exchange, Throwable ex);
550
}
551
```
552
553
**Usage Examples:**
554
555
```java
556
// Custom exception handler
557
@Component
558
public class CustomExceptionHandler implements DispatchExceptionHandler {
559
560
@Override
561
public Mono<HandlerResult> handleError(ServerWebExchange exchange, Throwable ex) {
562
if (ex instanceof ValidationException) {
563
return handleValidationException(exchange, (ValidationException) ex);
564
} else if (ex instanceof SecurityException) {
565
return handleSecurityException(exchange, (SecurityException) ex);
566
}
567
568
return handleGenericException(exchange, ex);
569
}
570
571
private Mono<HandlerResult> handleValidationException(ServerWebExchange exchange, ValidationException ex) {
572
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", ex.getErrors());
573
HandlerResult result = new HandlerResult(this, error, null);
574
575
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
576
return Mono.just(result);
577
}
578
579
private Mono<HandlerResult> handleSecurityException(ServerWebExchange exchange, SecurityException ex) {
580
ErrorResponse error = new ErrorResponse("ACCESS_DENIED", ex.getMessage());
581
HandlerResult result = new HandlerResult(this, error, null);
582
583
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
584
return Mono.just(result);
585
}
586
}
587
588
// Configure exception handling in WebFlux
589
@Configuration
590
public class ExceptionHandlingConfig {
591
592
@Bean
593
public WebExceptionHandler customWebExceptionHandler() {
594
return new CustomWebExceptionHandler();
595
}
596
597
@Order(-2) // Higher priority than default handlers
598
public static class CustomWebExceptionHandler implements WebExceptionHandler {
599
600
private final DispatchExceptionHandler exceptionHandler;
601
602
public CustomWebExceptionHandler(DispatchExceptionHandler exceptionHandler) {
603
this.exceptionHandler = exceptionHandler;
604
}
605
606
@Override
607
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
608
return exceptionHandler.handleError(exchange, ex)
609
.flatMap(result -> writeErrorResponse(exchange, result))
610
.onErrorResume(error -> writeGenericError(exchange, error));
611
}
612
}
613
}
614
```