Explain configureHttpMessageCodecs, addFormatters, and addArgumentResolvers in WebFluxConfigurer — what each customizes and how they differ.
answer
- Codecs = whole body (ServerCodecConfigurer)
- Formatters = string->type for path/param/header/form
- ArgumentResolvers = build whole method param (custom only)
- Formatters DON'T touch JSON body (that's Jackson/codec)
- Custom resolvers run after built-ins
basics
~10 sconfigureHttpMessageCodecs tunes body serialization/deserialization (via ServerCodecConfigurer). addFormatters registers Converter/Formatter for turning strings into types (path vars, params). addArgumentResolvers adds custom HandlerMethodArgumentResolvers for controller parameters the framework doesn't handle natively.
solid answer
~50 sThese three callbacks target different layers of request handling. configureHttpMessageCodecs(ServerCodecConfigurer) governs how request/response BODIES are read and written — you adjust default codecs (e.g. limits, Jackson mapper) via configurer.defaultCodecs() or register custom Encoder/Decoder via customCodecs(). addFormatters(FormatterRegistry) registers Converter, ConverterFactory, and Formatter beans used by the ConversionService to convert simple string inputs — @PathVariable, @RequestParam, form fields — into typed values (e.g. a String to a LocalDate or a domain id). addArgumentResolvers(ArgumentResolverConfigurer) plugs in custom HandlerMethodArgumentResolver implementations so controller methods can receive parameters the framework doesn't resolve out of the box (e.g. resolving a @CurrentUser argument from a header or the reactive security context). Rule of thumb: codecs = whole-body marshalling; formatters = string-to-type of individual values; argument resolvers = building an entire method parameter from the exchange. They compose additively and only supplement the built-in machinery.
code
java · 22 lines@Configuration
public class WebConfig implements WebFluxConfigurer {
// 1. body marshalling: raise/customize codecs
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024); // 2 MB buffer
// configurer.customCodecs().register(new ProtobufEncoder());
}
// 2. string -> type for @PathVariable/@RequestParam
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToProductIdConverter());
}
// 3. custom controller parameter
@Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
configurer.addCustomResolver(new CurrentUserArgumentResolver());
}
}go deeper
Know codecs handle bodies, formatters convert strings, argument resolvers build custom controller params.
Match each callback to its binding layer and know formatters don't touch JSON bodies.
Explain ordering (built-ins before custom resolvers), reactive non-blocking constraints, and when to reach for codec vs formatter.
Design cross-cutting parameter injection (e.g. current user) reactively without blocking, and decide codec-level vs binding-level customization for a given contract.
## Three layers, three callbacks Request handling in WebFlux converts an incoming `ServerWebExchange` into controller method arguments and the return value back into a response. Different pieces do different jobs; each has its own configurer callback. ### 1. configureHttpMessageCodecs(ServerCodecConfigurer) **Scope:** reading a full request body (`@RequestBody`, `ServerRequest.bodyToMono`) and writing a full response body. Codecs pair an `Encoder`/`Decoder` (reactive) into `HttpMessageReader`/`HttpMessageWriter`. - `configurer.defaultCodecs()` exposes knobs on the built-in codecs — e.g. supplying a custom `Jackson2ObjectMapperBuilder`/mapper, enabling logging of form/multipart data, or setting buffer limits. (Deep codec internals belong to the codecs leaf; here it's the *configuration surface*.) - `configurer.customCodecs().register(...)` adds your own `Encoder`/`Decoder` (e.g. Protobuf, CBOR, a bespoke format). - Because it operates on whole bodies, this is where content-type-driven (de)serialization is customized. ### 2. addFormatters(FormatterRegistry registry) **Scope:** converting individual **string** values to/from typed objects via the `FormattingConversionService`. Used for `@PathVariable`, `@RequestParam`, `@RequestHeader`, and form-field binding — NOT for JSON body fields (those go through the codec/Jackson). - Register a `Converter<S,T>` (one-directional), a `ConverterFactory`, or a `Formatter<T>` (parse + print, locale-aware, e.g. dates/currency). - Example: convert a path segment `"2026-07-22"` to `LocalDate`, or a slug to a domain enum/value object. ### 3. addArgumentResolvers(ArgumentResolverConfigurer configurer) **Scope:** producing an **entire** controller-method parameter from the `ServerWebExchange` when no built-in resolver applies. You add `HandlerMethodArgumentResolver` (or its sync variant `SyncHandlerMethodArgumentResolver`) implementations. - Only for **custom** resolvers; built-ins (`@RequestBody`, `@PathVariable`, `Mono<Principal>`, etc.) are already registered and run first. - Classic use: a `@CurrentUser User` parameter resolved from the reactive `SecurityContext`, or an object assembled from several headers. - The resolver's `supportsParameter` decides applicability; `resolveArgument` returns a `Mono<Object>`. ## How they differ (the exam answer) - **Codecs** = marshal the **body** as a whole (content-negotiated serialization). - **Formatters** = convert a single **string** to a type for path/query/header/form binding. - **Argument resolvers** = construct a whole **method parameter** from the exchange, beyond what framework binding covers. ## Gotchas - Formatters do NOT affect JSON body deserialization — that's Jackson via codecs. Registering a `Formatter<LocalDate>` won't change how a date inside a JSON body is parsed; configure Jackson (through the codec) for that. - Custom argument resolvers run AFTER the built-ins, so you cannot override, say, `@RequestBody` handling by adding a resolver; you'd customize the codec instead. - All three are additive; in Boot they work without `@EnableWebFlux`. - Reactive resolvers must be non-blocking and return `Mono`/`Flux`; blocking inside `resolveArgument` stalls the event loop.
- You registered a Formatter<LocalDate> but dates inside JSON request bodies still parse the old way. Why?FormatterRegistry only feeds the ConversionService used for string-based binding (path vars, request params, form fields). JSON body fields are deserialized by Jackson through the HTTP codec, which ignores Spring formatters. Configure the ObjectMapper via configureHttpMessageCodecs (or Jackson settings) instead.
- Can you override built-in @RequestBody handling by adding a custom argument resolver?No. Custom resolvers added via addArgumentResolvers/ArgumentResolverConfigurer run after the built-in ones, so the framework's @RequestBody resolver claims the parameter first. To change body handling you customize the codec (Encoder/Decoder) rather than an argument resolver.
saying these in an interview costs you the question
- Thinking formatters affect JSON body (de)serialization
- Believing a custom argument resolver can override built-in @RequestBody/@PathVariable resolvers
- Doing blocking work inside a reactive HandlerMethodArgumentResolver