pkslow.com 南瓜慢说

  • AllArticles
  • Container
  • Spring
  • Life
  • Cloud
  • Collections
  • About
  • GitHub

  • Search
Terraform101 English Terraform Middleware config Go Private Kubernetes pkslow Test HTTPS Redis Docker Mac Plan Stream MongoDB Spring DevOps JVM String Map Set List Performance Email Springboot JavaCollections ArrayList Java

在Spring WebFlux的任何地方获取Request对象

Created on: 2021-01-12 | Category: Springboot | 0 | View: 2446

1 不一样的世界

在常规的Spring Web项目中,我们要获取Request对象是非常方便的,不少库都提供了静态方法来获取。获取代码如下:

ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
// get the request
HttpServletRequest request = requestAttributes.getRequest();

在类RequestContextHolder提供了静态方法,也就意味着你可以在任何地方调用。而它使用了ThreadLocal来保存Request对象,也就是不同线程是可以获取各自的Request对象。

但在响应式WebFlux的世界里,并没有提供类似的Holder类,而WebFlux是无法感知线程的,任何一个线程可以在任何时候处理任何请求,如果它觉得切换当前线程更有效率,它就会这么做。但在Servlet Based的应用里,它会为某个请求安排一个线程去处理完整个过程。

这个巨大的差别,意味着不能简单地通过ThreadLocal来保存和获取Request了。

2 先保存,再获取

为了在后面可以方便获得Request对象,我们就需要在开始的时候把它存在一个可以使用、并且是相同scope的容器里。这里需要解决两个关键问题:

(1)Request对象从何而来;

(2)存在哪里?

针对问题(1), 我们可以回想什么时候会出现Request对象,最容易想得到的就是WebFilter了,它的方法签名如下:

public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain);

我们可以通过ServerWebExchange直接获取到Request对象:

ServerHttpRequest request = exchange.getRequest();

而因为Filter是可以先于应用逻辑执行的,所以满足要求,问题(1)解决。

针对问题(2),需要一个与Reavtive请求相同范围的容器,reactor.util.context.Context可以满足需求。查看reactor的官方文档(https://projectreactor.io/docs/core/release/reference/#context )可见下面这段话:

Since version 3.1.0, Reactor comes with an advanced feature that is somewhat comparable to ThreadLocal but can be applied to a Flux or a Mono instead of a Thread. This feature is called Context.

并且官网也给出了为何ThreadLocal在某些场景不适用的解释,有兴趣可以看看。

3 代码实现

3.1 WebFilter获取并保存

首先,在WebFilter中获取Request对象并保存,代码如下:

@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));
    }
}

从ServerWebExchange中获取到ServerHttpRequest对象,再通过put方法把它放进Context里。

3.2 工具类Holder

实现一个工具类来提供静态方法,在Filter后的任何场景都可以使用:

public class ReactiveRequestContextHolder {
    public static final Class<ServerHttpRequest> CONTEXT_KEY = ServerHttpRequest.class;

    public static Mono<ServerHttpRequest> getRequest() {
        return Mono.subscriberContext()
                .map(ctx -> ctx.get(CONTEXT_KEY));
    }
}

3.3 在Controller中使用

我们尝试在Controller中使用ReactiveRequestContextHolder来获取Request:

@RestController
public class GetRequestController {

    @RequestMapping("/request")
    public Mono<String> getRequest() {
        return ReactiveRequestContextHolder.getRequest()
                .map(request -> request.getHeaders().getFirst("user"));
    }
}

上面方法获取了Request对象,然后再获取了Request中的Header。

启动应用,测试如下:

$ curl http://localhost:8088/request -H 'user: pkslow'
pkslow

$ curl http://localhost:8088/request -H 'user: larry'
larry

$ curl http://localhost:8088/request -H 'user: www.pkslow.com'
www.pkslow.com

可以成功获取请求头user。

4 总结

代码请查看:https://github.com/LarryDpk/pkslow-samples


参考资料:

Getting Request Object in Spring Webflux elastic thread


Code for all: GitHub

欢迎关注微信公众号<南瓜慢说>,将持续为你更新...

file

Recommendations:
Cloud Native
Terraform
Container: Docker/Kubernetes
Spring Boot / Spring Cloud
Https
如何制定切实可行的计划并好好执行

  • Author 作者: LarryDpk 南瓜慢说
  • Link 链接: https://www.pkslow.com/archives/spring-webflux-get-request
  • 版权声明: 本博客所有文章除特别声明外,不可转载!
# Terraform101 # English # Terraform # Middleware # config # Go # Private # Kubernetes # pkslow # Test # HTTPS # Redis # Docker # Mac # Plan # Stream # MongoDB # Spring # DevOps # JVM # String # Map # Set # List # Performance # Email # Springboot # JavaCollections # ArrayList # Java
Terraform101 English Terraform Middleware config Go Private Kubernetes pkslow Test HTTPS Redis Docker Mac Plan Stream MongoDB Spring DevOps JVM String Map Set List Performance Email Springboot JavaCollections ArrayList Java
最好的Kubernetes客户端Java库fabric8io,快来自定义你的操作
详细记录用kubeadm在Ubuntu上安装Kubernetes集群
  • Contents
  • Site Overview
南瓜慢说

南瓜慢说

多年Java开发,主要专注后端技术:Java/Spring/Springboot/微服务/大数据等。

多读书,多分享;多写作,多整理。

241 Posts
9 Categories
30 Tags
RSS
0%
© 2020 — 2022 南瓜慢说 pkslow The WebSite keeping alive:   粤ICP备20036375号