說明
在 Spring boot web
中我們可以通過 RequestContextHolder
很方便的獲取 request
。
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// 獲取 request
HttpServletRequest request = requestAttributes.getRequest();
複製程式碼
不再需要通過引數傳遞 request
。在 Spring webflux 中並沒提供該功能,使得我們在 Aop 或者一些其他的場景中獲取 request
變成了一個奢望???
尋求解決方案
首先我想到的是看看 spring-security
中是否有對於的解決方案,因為在 spring-security
中我們也是可以通過 SecurityContextHolder
很方便快捷的獲取當前登入的使用者資訊。
找到了 ReactorContextWebFilter,我們來看看 security 中他是怎麼實現的。 github.com/spring-proj…
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));
}
}
複製程式碼
原始碼裡面我們可以看到 他利用一個 Filter,chain.filter(exchange)
的返回值 Mono 呼叫了 subscriberContext 方法。
那麼我們就去了解一下這個 reactor.util.context.Context
。找到 reactor 官方文件中的 context 章節:projectreactor.io/docs/core/r…
大意是:從 Reactor 3.1.0 開始提供了一個高階功能,可以與 ThreadLocal 媲美,應用於 Flux 和 Mono 的上下文工具 Context。更多請大家查閱官方文件,對英文比較牴觸的朋友可以使用 google 翻譯。
mica 中的實現
mica 中的實現比較簡單,首先是我們的 ReactiveRequestContextFilter
:
/**
* ReactiveRequestContextFilter
*
* @author L.cm
*/
@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));
}
}
複製程式碼
在 Filter
中直接將 request
儲存到 Context
上下文中。
ReactiveRequestContextHolder 工具:
/**
* ReactiveRequestContextHolder
*
* @author L.cm
*/
public class ReactiveRequestContextHolder {
static final Class<ServerHttpRequest> CONTEXT_KEY = ServerHttpRequest.class;
/**
* Gets the {@code Mono<ServerHttpRequest>} from Reactor {@link Context}
* @return the {@code Mono<ServerHttpRequest>}
*/
public static Mono<ServerHttpRequest> getRequest() {
return Mono.subscriberContext()
.map(ctx -> ctx.get(CONTEXT_KEY));
}
}
複製程式碼
怎麼使用呢?
mica 中對未知異常處理,從 request
中獲取請求的相關資訊
@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) {
// 具體業務邏輯
}
複製程式碼
WebClient 透傳 request
中的 header
此示例來源於開源中國問答中筆者的回覆: 《如何在gateway 中獲取 webflux的 RequestContextHolder》
@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);
});
}
複製程式碼
上段程式碼是透傳 web 中的 request
中的 user_agent
請求頭到 WebClient
中。
開源推薦
mica
Spring boot 微服務核心元件集:gitee.com/596392912/m…Avue
一款基於vue可配置化的神奇框架:gitee.com/smallweigit…pig
宇宙最強微服務(架構師必備):gitee.com/log4j/pigSpringBlade
完整的線上解決方案(企業開發必備):gitee.com/smallc/Spri…IJPay
支付SDK讓支付觸手可及:gitee.com/javen205/IJ…
關注我們
掃描上面二維碼,更多精彩內容每天推薦!
轉載宣告
如夢技術對此篇文章有最終所有權,轉載請註明出處,參考也請註明,謝謝!