Spring boot webflux 中實現 RequestContextHolder

說明

Spring boot web 中咱們能夠經過 RequestContextHolder 很方便的獲取 requestvue

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

// 獲取 request
HttpServletRequest request = requestAttributes.getRequest();
複製代碼

再也不須要經過參數傳遞 request。在 Spring webflux 中並沒提供該功能,使得咱們在 Aop 或者一些其餘的場景中獲取 request 變成了一個奢望???java

尋求解決方案

首先我想到的是看看 spring-security 中是否有對於的解決方案,由於在 spring-security 中咱們也是能夠經過 SecurityContextHolder 很方便快捷的獲取當前登陸的用戶信息。react

找到了 ReactorContextWebFilter,咱們來看看 security 中他是怎麼實現的。 github.com/spring-proj…git

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…github

大意是:從 Reactor 3.1.0 開始提供了一個高級功能,能夠與 ThreadLocal 媲美,應用於 Flux 和 Mono 的上下文工具 Context。更多請你們查閱官方文檔,對英文比較抵觸的朋友能夠使用 google 翻譯。web

mica 中的實現

mica 中的實現比較簡單,首先是咱們的 ReactiveRequestContextFilterspring

/** * 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 工具:app

/** * 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 中。

開源推薦

關注咱們

如夢技術-公衆號.jpg

掃描上面二維碼,更多精彩內容天天推薦!

轉載聲明

如夢技術對此篇文章有最終全部權,轉載請註明出處,參考也請註明,謝謝!

相關文章
相關標籤/搜索