When would you use Reactor Context versus ServerWebExchange attributes to carry request-scoped data in WebFlux?
answer
- exchange attributes = web tier, need the exchange
- Reactor Context = whole pipeline, layer-agnostic
- service code has no exchange -> use Context
- Security/tracing read from Context
- WebFilter derives from exchange, seeds Context
basics
~20 sUse ServerWebExchange attributes for web-layer data available where you hold the exchange (filters, controllers). Use Reactor Context for data that must flow deep into the reactive pipeline and service layer where no exchange is passed, and for anything Reactor/Security reads from Context.
solid answer
~40 sServerWebExchange.getAttributes() is a mutable map on the exchange object — convenient when you already have the exchange (WebFilters, handler methods, HandlerMapping). But service-layer code usually doesn't receive the exchange, so those attributes aren't reachable there. Reactor Context, by contrast, propagates through the reactive chain to any operator regardless of layer, and is the mechanism Reactor-based infrastructure uses (ReactiveSecurityContextHolder, tracing). Rule of thumb: exchange attributes for web-tier coordination between filters/handlers; Reactor Context for cross-cutting values (tenant, trace id, principal) that pure reactive service code must read without depending on the web layer. Often a WebFilter reads/derives from the exchange and seeds the Reactor Context via contextWrite so lower layers stay web-agnostic.
code
java · 10 lines@Component
class RequestScopeFilter implements WebFilter {
static final String TENANT = "tenant";
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String tenant = exchange.getAttributeOrDefault("resolvedTenant", "public");
// bridge a web-layer value into the reactive pipeline for services
return chain.filter(exchange).contextWrite(c -> c.put(TENANT, tenant));
}
}go deeper
May not know ServerWebExchange attributes exist.
Knows both exist but may blur when to use which.
Should articulate the layer-coupling trade-off and the filter-seeds-Context pattern.
Should discuss decoupling, that Security/tracing mandate Context, and avoiding leaking the exchange into lower layers.
## The two mechanisms ### ServerWebExchange attributes `ServerWebExchange` is the WebFlux equivalent of `HttpServletRequest`+`HttpServletResponse`. It has a **mutable** attribute map: `exchange.getAttributes()`, `getAttribute(name)`, `getAttributeOrDefault(...)`. It's the natural place for **web-layer** coordination — e.g. a `WebFilter` stashing something a controller or another filter reads, or framework internals (the matched `PathPattern`, etc.). Limitation: you can only read it **where you have the exchange**. Filters and `@Controller` handler methods can inject it, but your domain/service beans typically don't (and shouldn't) receive a `ServerWebExchange` — coupling the service layer to the web tier is poor design. ### Reactor Context An immutable store on the subscription that reaches **any operator anywhere in the reactive chain**, including deep in service code that only sees `Mono`/`Flux`. It's also what reactive infrastructure reads: `ReactiveSecurityContextHolder`, Micrometer Tracing, etc. Limitation: mutable-map convenience is gone (immutable, upstream propagation rules), and you must place `contextWrite` correctly. ## Decision guide | Need | Prefer | |---|---| | Data shared between filters/handlers that all hold the exchange | Exchange attributes | | Web-framework metadata (matched route, cached body) | Exchange attributes (often framework-managed) | | Value consumed by pure reactive service code with no exchange | Reactor Context | | Tenant / trace id / locale / principal used cross-layer | Reactor Context | | Interop with ReactiveSecurityContextHolder / tracing | Reactor Context (required) | ## Common combined pattern A `WebFilter` extracts data from the request (a header on the exchange) and **seeds the Reactor Context** so downstream, web-agnostic code can read it: ```java return chain.filter(exchange) .contextWrite(ctx -> ctx.put(TENANT, resolveTenant(exchange))); ``` This keeps the web concern (parsing the header) in the filter while exposing a clean, framework-independent value to the service layer via Context. ## Gotchas - Exchange attributes are mutable and not thread-safe for concurrent writes; treat them as single-writer. - Reactor Context won't be visible if the write is misplaced relative to the read (upstream rule). - Don't smuggle the whole `ServerWebExchange` into Reactor Context just to reach it in services — extract the specific values instead, keeping layers decoupled.
- Why not just pass ServerWebExchange into your service beans?It couples the service/domain layer to the web tier, hurting testability and reuse. Extract the needed values (tenant, trace id) and pass them via Reactor Context or method parameters instead.
- Can service-layer reactive code read exchange attributes?Only if it is handed the ServerWebExchange, which is discouraged. Otherwise no — that's precisely when you seed Reactor Context from a filter.
saying these in an interview costs you the question
- Storing the whole ServerWebExchange in Reactor Context to reach it in services
- Assuming exchange attributes are visible deep in the reactive service chain
- Treating exchange attributes as immutable/thread-safe