How to Access HttpServletRequest in Spring WebFlux Using Reactor Context
This article explains why Spring WebFlux lacks RequestContextHolder, explores the ReactorContextWebFilter solution from Spring Security, and demonstrates a custom ReactiveRequestContextFilter and helper class that store and retrieve the request via Reactor's Context for exception handling and WebClient header propagation.
Explanation
In Spring Boot Web we can obtain the
HttpServletRequestvia
RequestContextHolder. In Spring WebFlux this feature is missing, making it difficult to retrieve the request in AOP or other scenarios.
<code>ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();</code>
<code>// Get request</code>
<code>HttpServletRequest request = requestAttributes.getRequest();</code>We first look for a solution in
spring-security. The
ReactorContextWebFiltershows how security stores the context.
<code>public class ReactorContextWebFilter implements WebFilter {
private final ServerSecurityContextRepository repository;
public ReactorContextWebFilter(ServerSecurityContextRepository repository) {
Assert.notNull(repository, "repository cannot be null");
this.repository = repository;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange)
.subscriberContext(c -> c.hasKey(SecurityContext.class) ? c : withSecurityContext(c, exchange));
}
private Context withSecurityContext(Context mainContext, ServerWebExchange exchange) {
return mainContext.putAll(this.repository.load(exchange)
.as(ReactiveSecurityContextHolder::withSecurityContext));
}
}</code>The filter uses
reactor.util.context.Context, a ThreadLocal‑like feature introduced in Reactor 3.1.
Implementation in mica
The mica project provides a simple
ReactiveRequestContextFilterthat stores the
ServerHttpRequestin the Reactor Context.
<code>@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveRequestContextFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
return chain.filter(exchange)
.subscriberContext(ctx -> ctx.put(ReactiveRequestContextHolder.CONTEXT_KEY, request));
}
}</code>The helper
ReactiveRequestContextHolderretrieves the request from the Context.
<code>public class ReactiveRequestContextHolder {
static final Class<ServerHttpRequest> CONTEXT_KEY = ServerHttpRequest.class;
public static Mono<ServerHttpRequest> getRequest() {
return Mono.subscriberContext()
.map(ctx -> ctx.get(CONTEXT_KEY));
}
}</code>How to Use It
Exception handling with the stored request
<code>@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Mono<?> handleError(Throwable e) {
log.error("未知异常", e);
return ReactiveRequestContextHolder.getRequest()
.doOnSuccess(r -> publishEvent(r, e))
.flatMap(r -> Mono.just(R.fail(SystemCode.FAILURE)));
}
private void publishEvent(ServerHttpRequest request, Throwable error) {
// business logic
}</code>WebClient header propagation
<code>@GetMapping("/test")
@ResponseBody
public Mono<String> test() {
WebClient webClient = testClient();
return webClient.get().uri("").retrieve().bodyToMono(String.class);
}
@Bean
public WebClient testClient() {
return WebClient.builder()
.filter(testFilterFunction())
.baseUrl("https://www.baidu.com")
.build();
}
private ExchangeFilterFunction testFilterFunction() {
return (request, next) -> ReactiveRequestContextHolder.getRequest()
.flatMap(r -> {
ClientRequest clientRequest = ClientRequest.from(request)
.headers(headers -> headers.set(HttpHeaders.USER_AGENT,
r.getHeaders().getFirst(HttpHeaders.USER_AGENT)))
.build();
return next.exchange(clientRequest);
});
}</code>These examples demonstrate how to retrieve the original request for logging, error handling, and forwarding headers when using Spring WebFlux.
References
[1]: https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/server/context/ReactorContextWebFilter.java#L43
[2]: https://projectreactor.io/docs/core/release/reference/#context
[3]: https://www.oschina.net/question/2315449_2305069
Java Architecture Diary
Committed to sharing original, high‑quality technical articles; no fluff or promotional content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.