Spring boot webflux 中实现 RequestContextHolder
说明
在 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 中他是怎么实现的。
https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/server/context/ReactorContextWebFilter.java#L43
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 章节:https://projectreactor.io/docs/core/release/reference/#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<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 微服务核心组件集:https://gitee.com/596392912/mica -
Avue
一款基于vue可配置化的神奇框架:https://gitee.com/smallweigit/avue -
pig
宇宙最强微服务(架构师必备):https://gitee.com/log4j/pig -
SpringBlade
完整的线上解决方案(企业开发必备):https://gitee.com/smallc/SpringBlade -
IJPay
支付SDK让支付触手可及:https://gitee.com/javen205/IJPay
转载声明
如梦技术对此篇文章有最终所有权,转载请注明出处,参考也请注明,谢谢!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
后通用芯片时代: 专用芯片兴起背后的经济学
最近,MIT学者Neil Thompson和Svenja Spanuth发表了一篇名为“The Decline of Computers as a General Purpose Technology” 的working paper,重点分析了为什么摩尔定律的终结与深度学习的兴起会加速计算技术的专用化趋势。我和Neil有一面之缘,他在伯克利哈斯商学院的博士课题就是研究IT产业对社会经济和生产力的影响。50多页的论文读着一气呵成,遂整理读书笔记加上一些我的注解,以飨读者。 众所周知,生产率(productivity)是衡量经济增长和经济水平的一个很重要的因素。计算机自20世纪中叶诞生以来用前所未有的速度改变着我们的生活,以美国为例,计算机技术带动了自1974年以来的1/3生产率增长,居功至伟。从产业发展的角度来看,通用计算的进步与普惠
- 下一篇
深度揭秘:机器学习对软件开发带来哪些影响?
机器学习有望从根本上改变软件开发的本质,这也许是自FORTRAN和LISP被发明以来软件开发领域改变最大的一次。这些变化对数百万正在从事软件开发的人而言,意味着什么呢?失业?裁员?现有的软件开发将变得面目全非? 自20世纪70年代以来,我们尽可能的构建了足够多的软件。我们有高级语言,低级语言,脚本语言以及用于构建和测试软件的工具,但我们利用这些工具做的事情却没有发生太大变化。我们现在拥有的语言和工具比50年前要好得多,但它们本质上是一样的。我们仍然使用代码编辑器,但这些编辑器变得更花哨了:他们有彩色的高亮,变量名补全,它们有时可以帮助我们完成重构等任务,但他们仍然是emacs和vi的后代。面向对象编程代表了一种不同的编程风格,但从某种本质上而言并不是“全新”的事物,对于函数式编程我们可以一直追溯到50年代。 未来我们将专注于机器学习而
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS关闭SELinux安全模块
- Docker安装Oracle12C,快速搭建Oracle学习环境