博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring boot webflux 中实现 RequestContextHolder
阅读量:6335 次
发布时间:2019-06-22

本文共 4199 字,大约阅读时间需要 13 分钟。

说明

Spring boot web 中我们可以通过 RequestContextHolder 很方便的获取 request

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 获取 requestHttpServletRequest request = requestAttributes.getRequest();

不再需要通过参数传递 request。在 Spring webflux 中并没提供该功能,使得我们在 Aop 或者一些其他的场景中获取 request 变成了一个奢望???

寻求解决方案

首先我想到的是看看 spring-security 中是否有对于的解决方案,因为在 spring-security 中我们也是可以通过 SecurityContextHolder 很方便快捷的获取当前登录的用户信息。

找到了 ReactorContextWebFilter,我们来看看 security 中他是怎么实现的。

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
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 章节:

大意是:从 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
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
CONTEXT_KEY = ServerHttpRequest.class; /** * Gets the {@code Mono
} from Reactor {@link Context} * @return the {@code Mono
} */ public static Mono
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

此示例来源于开源中国问答中笔者的回复:

@GetMapping("/test")@ResponseBodypublic Mono
test() { WebClient webClient = testClient(); return webClient.get().uri("").retrieve().bodyToMono(String.class);}@Beanpublic 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 微服务核心组件集:
  • Avue 一款基于vue可配置化的神奇框架:
  • pig 宇宙最强微服务(架构师必备):
  • SpringBlade 完整的线上解决方案(企业开发必备):
  • IJPay 支付SDK让支付触手可及:

转载声明

如梦技术对此篇文章有最终所有权,转载请注明出处,参考也请注明,谢谢!

你可能感兴趣的文章
LeetCode - 26. Remove Duplicates from Sorted Array
查看>>
坐在马桶上看算法:只有五行的Floyd最短路算法
查看>>
213. House Robber II 打家劫舍 IIJava
查看>>
后缀自动机多图详解(代码实现)
查看>>
java位移运算符 转
查看>>
转:strcpy实现的考察要点
查看>>
【转】Map/Reduce简介
查看>>
LOB
查看>>
js验证姓名和身份证号
查看>>
Solr空格默认值是AND还是OR
查看>>
(转)SQL SERVER 生成建表脚本
查看>>
关于js单线程的解释
查看>>
python 内置函数bytearray
查看>>
对 Java Integer.valueOf() 的一些了解
查看>>
python csv文件转换成xml, 构建新xml文件
查看>>
SQL Server 配置管理器
查看>>
Web测试中,各类web控件测试点(转)
查看>>
JAVA-API Dom4J解析xml/OPML & Rome解析RSS & QRCode编码解码
查看>>
Unity5 新功能解析--GI(全局光)
查看>>
〖Linux〗Kubuntu文件管理器单例的设置(即:一个工作区只一个文件管理器)
查看>>