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

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

以上就是如何在Spring WebFlux的任何地方获取Request对象的详细内容,更多关于Spring WebFlux获取Request对象的资料请关注我们其它相关文章!

(0)

相关推荐

  • 使用 Spring Boot 2.0 + WebFlux 实现 RESTful API功能

    概述 什么是 Spring WebFlux, 它是一种异步的, 非阻塞的, 支持背压(Back pressure)机制的Web 开发框架. 要深入了解 Spring WebFlux, 首先要了知道 Reactive Stream . 另一种编程姿势, 和命令式编程相对的姿势. WebFlux 支持两种编程风(姿)格(势) 使用 @Controller 这种基于注解的姿势, 与Sring MVC的姿势相同 基于Java 8 Lambda的函数式编程风格 注意: 上面只是两种编程的姿势, 和"普通话

  • 解决spring-boot2.0.6中webflux无法获得请求IP的问题

    这几天在用 spring-boot 2 的 webflux 重构一个工程,写到了一个需要获得客户端请求 IP 的地方,发现写不下去了,在如下的 Handler(webflux 中 Handler 相当于 mvc 中的 Controller)中 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; im

  • SpringBoot2使用WebFlux函数式编程的方法

    本文只是简单使用SpringBoot2使用WebFlux的函数式编程简单使用,后续会继续写关于Webflux相关的文章. 最近一直在研究WebFlux,后续会陆续出一些相关的文章. 首先看一下Srping官网上的一张图,对比一下SpringMvc和Spring WebFlux,如图: 在查看一下WebFlux的官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html,W

  • 详解Spring Boot2 Webflux的全局异常处理

    本文首先将会回顾Spring 5之前的SpringMVC异常处理机制,然后主要讲解Spring Boot 2 Webflux的全局异常处理机制. SpringMVC的异常处理 Spring 统一异常处理有 3 种方式,分别为: 使用 @ExceptionHandler 注解 实现 HandlerExceptionResolver 接口 使用 @controlleradvice 注解 使用 @ExceptionHandler 注解 用于局部方法捕获,与抛出异常的方法处于同一个Controller类

  • Spring Boot webflux使用方法解析

    1.同步阻塞IO模型 当容器中只有三个线程接收请求,当有四个请求过来的时候,就会Block住,得不到及时的响应 2.异步非阻塞式IO模型 Spring Boot webflux是异步非阻塞式IO模型,容器线程将耗时的任务(IO密集型任务)交给work线程来处理 3.webflux应用场景 4.webflux与springmvc异同点 5.webflux使用建议 1).如果当前项目比较稳定,没必要切换.如果要切换最好切换整套技术栈 2).如果只是个人对新技术感兴趣,可以在一些简单小型项目中使用研究

  • 聊聊spring boot的WebFluxTagsProvider的使用

    序 本文主要研究一下webflux的WebFluxTagsProvider WebFluxTagsProvider spring-boot-actuator-2.1.5.RELEASE-sources.jar!/org/springframework/boot/actuate/metrics/web/reactive/server/WebFluxTagsProvider.java @FunctionalInterface public interface WebFluxTagsProvider

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

    1 不一样的世界 在常规的Spring Web项目中,我们要获取Request对象是非常方便的,不少库都提供了静态方法来获取.获取代码如下: ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); // get the request HttpServletRequest request = requestAttribut

  • Spring实现在非controller中获取request对象

    目录 在非controller中获取request对象 首先在web.xml中添加一个监听器 然后在程序中就可以使用 非controller层获取response和request对象 如下所示 在非controller中获取request对象 在Controller中,我们可以直接在参数中加上HttpServletRequest request,springMVC就会自动将request绑定进参数,而如果我们想在其他地方获取request就要如下这么做了. 首先在web.xml中添加一个监听器

  • spring中通过ApplicationContext getBean获取注入对象的方法实例

    用SpringContextUtil实现ApplicationContextAware package util; import java.util.Locale; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; pub

  • Spring Boot中优雅的获取yml文件工具类

    如何在spring boot中优雅的获取.yml文件工具类呢 代码如下: package com.common.base.utils.base; import com.common.base.generator.ResourceManager; import org.yaml.snakeyaml.Yaml; import java.io.InputStream; import java.util.HashMap; import java.util.Map; /** * yml文件工具类 */ p

  • Spring中如何获取request的方法汇总及其线程安全性分析

    前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性.下面话不多说了,来一起看看详细的介绍吧. 概述 在使用Spring MVC开发Web系统时,经常需要在处理请求时使用request对象,比如获取客户端ip地址.请求的url.header中的属性(如cookie.授权信息).body中的数据等.由于在Spring MVC中,处理请求的Controller.Service等对象都是单例的,因此获取request对象时最需要注意的问题,便是

  • Spring Security中如何获取AuthenticationManager对象

    有时需要使用AuthenticationManager(以下简称Manager)对象,可是这个对象不是Bean,没有直接保存在Spring的Bean库中.那么如何获取Spring Security中的这个对象呢? 在Spring Security 5.7.0-M2之前,通常会扩展WebSecurityConfigurerAdapter(以下简称Adapter)类来自定义网络安全配置.Adapter类中有一个方法authenticationManager()可以提供Manager对象.但是从Spr

  • 如何在Spring boot加入shiro支持

    这篇文章主要介绍了如何在Spring boot加入shiro支持,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在项目添加依赖 <!-- shiro spring. --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.

  • spring webflux自定义netty 参数解析

    目录 自定义 webflux 容器配置 解决方案 初识Spring WebFlux Spring Web新的改变 Spring WebFlux的特性 1.异步非阻塞 2.响应式(reactive)编程 3.适配多种web容器 Spring WebFlux简单实践 1.工程创建 2.Controller中与SpringMVC的对比 3.异步非阻塞的体现 4.添加数据库支持 5.Dao的编写 6.Controller的编写 7.响应式编程Handler的编写 8.响应式编程Route的编写 自定义

  • 如何在Spring data中使用r2dbc详解

    前言 上篇文章我们讲到了怎么在Spring webFlux中使用r2dbc,今天我们看一下怎么使用spring-data-r2dbc这个Spring data对r2dbc的封装来进行r2dbc操作. 依赖关系 要使用Spring-datea-r2dbc需要配置下面的依赖关系: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>sp

  • 理解 MyBatis 是如何在 Spring 容器中初始化的

    MyBatis 初始化过程就是生成一些必须的对象放到 Spring 容器中.问题是这个过程到底生成了哪些对象?当遇到 MyBatis 初始化失败时,如何正确的找到分析问题的切入点?本文将针对这些问题进行介绍. 本文基于 MyBatis 3 和 Spring,假设读者已经知道如何使用 Maven 和 MyBatis,以及了解 Spring 的容器机制. 一.Mybatis 三件套 我们知道 MyBatis 的主要功能是由 SqlSessionFactory 和 Mapper 两者提供的,初始化 M

随机推荐