Spring RequestContextHolder 和 WebTestClient

2023-12-15

我正在使用春天RequestContextHolder在控制器中并且工作正常。但在单元测试中我得到java.lang.IllegalStateException using WebTestClient。这是一个例子:

package demo.reactive.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import reactor.core.publisher.Mono;

@RestController
public class FooController {

  @GetMapping("/foo")
  public ResponseEntity<Mono<String>> foo() {

    String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();

    return ResponseEntity.ok(Mono.just(sessionId));
  }
}
package demo.reactive;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import demo.reactive.controller.FooController;

@WebFluxTest(FooController.class)
class DemoReactiveApplicationTests {

  @Autowired private WebTestClient client;

  @Test
  void contextLoads() {
    client.get().uri("/foo").exchange().expectStatus().isOk();
  }
}

java.lang.IllegalStateException:未找到线程绑定请求:您是指实际 Web 请求之外的请求属性,还是在原始接收线程之外处理请求?如果您实际上在 Web 请求中进行操作并且仍然收到此消息,则您的代码可能在 DispatcherServlet 之外运行:在这种情况下,请使用 RequestContextListener 或 RequestContextFilter 公开当前请求。 在 org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE] 抑制:reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 在以下站点发现错误: |_ 检查点 ⇢ HTTP GET "/foo" [ExceptionHandlingWebHandler] 堆栈跟踪: 在 org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE] 在 demo.reactive.controller.FooController.foo(FooController.java:15) ~[classes/:na] 在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(本机方法) ~[na:na] 在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] 在 java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] 在 java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] 在 org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:147) ~[spring-webflux-5.2.5.RELEASE.jar:5.2.5.RELEASE] 在reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:151)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:48)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:153)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxSwitchIfEmpty $SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76)~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxConcatMap $ConcatMapImmediate.innerNext(FluxConcatMap.java:274)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxConcatMap $ConcatMapInner.onNext(FluxConcatMap.java:851)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:173)~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2274)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:132)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2082)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1956)~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:145)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.Mono.subscribe(Mono.java:4210)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxConcatMap $ConcatMapImmediate.drain(FluxConcatMap.java:441)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxConcatMap $ConcatMapImmediate.onSubscribe(FluxConcatMap.java:211)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.Mono.subscribe(Mono.java:4210)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172)~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在 org.springframework.test.web.reactive.server.HttpHandlerConnector.lambda$doConnect$1(HttpHandlerConnector.java:97) ~[spring-test-5.2.5.RELEASE.jar:5.2.5.RELEASE] 在 org.springframework.mock.http.client.reactive.MockClientHttpRequest.lambda$null$2(MockClientHttpRequest.java:121) ~[spring-test-5.2.5.RELEASE.jar:5.2.5.RELEASE] 在reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.Mono.subscribe(Mono.java:4210)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxConcatIterable $ConcatIterableSubscriber.onComplete(FluxConcatIterable.java:146)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.FluxConcatIterable.subscribe(FluxConcatIterable.java:60)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoFromFluxOperator.subscribe(MonoFromFluxOperator.java:72)~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在 org.springframework.test.web.reactive.server.HttpHandlerConnector.doConnect(HttpHandlerConnector.java:108) ~[spring-test-5.2.5.RELEASE.jar:5.2.5.RELEASE] 在 org.springframework.test.web.reactive.server.HttpHandlerConnector.lambda$connect$0(HttpHandlerConnector.java:79) ~[spring-test-5.2.5.RELEASE.jar:5.2.5.RELEASE] 在reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.Mono.subscribe(Mono.java:4210)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.run(MonoSubscribeOn.java:124)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37)〜[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] 在 java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na] 在 java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na] 在 java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na] 在 java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na] 在 java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

我怎样才能让单元测试工作。或者它甚至不应该使用RequestContextHolder与WebFlux?


Spring RequestContextHolder不能与 Spring Webflux 一起使用。RequestContextHolder主要作品使用java.lang.ThreadLocal。这显然不适合 Reactor 架构。

话虽如此,我们可以使用反应堆实现替代方案Mono#subscriberContext()操作员。创建可重用解决方案的最佳方法是实现 springWebFilter获取请求属性并将其设置在反应器上下文中。

public class WebRequestAttributesContextFilter implements WebFilter {

  public static final String WEB_REQUEST_ATTRIBUTES = "WebRequestAttributes";

  @Override
  public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
    return webFilterChain.filter(serverWebExchange)
        .subscriberContext(context -> context.put(WEB_REQUEST_ATTRIBUTES, serverWebExchange.getAttributes()));
  }
}

然后可以从请求处理管道中任何位置的反应器上下文中读取属性(在您的情况下为 sessionId),如下所示:

@GetMapping("/foo")
public Mono<String> helloWorld() {
  return Mono.subscriberContext().map(context -> (String) context.<Map<String, Object>>get(
          WebRequestAttributesContextFilter.WEB_REQUEST_ATTRIBUTES
      ).get("session"));
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring RequestContextHolder 和 WebTestClient 的相关文章

  • 从文件夹中删除文件的单元测试方法

    我们有一个方法 它将文件夹名称和天数作为参数 public void Delete string folder int days var files Directory GetFiles folder foreach var file in
  • 在画布上绘图

    我正在编写一个 Android 应用程序 它可以在视图的 onDraw 事件上直接绘制到画布上 我正在绘制一些涉及单独绘制每个像素的东西 为此我使用类似的东西 for int x 0 x lt xMax x for int y 0 y lt
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa
  • 制作一个交互式Windows服务

    我希望我的 Java 应用程序成为交互式 Windows 服务 用户登录时具有 GUI 的 Windows 服务 我搜索了这个 我发现这样做的方法是有两个程序 第一个是服务 第二个是 GUI 程序并使它们进行通信 服务将从 GUI 程序获取
  • JAXb、Hibernate 和 beans

    目前我正在开发一个使用 Spring Web 服务 hibernate 和 JAXb 的项目 1 我已经使用IDE hibernate代码生成 生成了hibernate bean 2 另外 我已经使用maven编译器生成了jaxb bean
  • Android:捕获的图像未显示在图库中(媒体扫描仪意图不起作用)

    我遇到以下问题 我正在开发一个应用程序 用户可以在其中拍照 附加到帖子中 并将图片保存到外部存储中 我希望这张照片也显示在图片库中 并且我正在使用媒体扫描仪意图 但它似乎不起作用 我在编写代码时遵循官方的Android开发人员指南 所以我不
  • INSERT..RETURNING 在 JOOQ 中不起作用

    我有一个 MariaDB 数据库 我正在尝试在表中插入一行users 它有一个生成的id我想在插入后得到它 我见过this http www jooq org doc 3 8 manual sql building sql statemen
  • 多个 Maven 配置文件激活多个 Spring 配置文件

    我想在 Maven 中构建一个环境 在其中我想根据哪些 Maven 配置文件处于活动状态来累积激活多个 spring 配置文件 目前我的 pom xml 的相关部分如下所示
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • 列出jshell中所有活动的方法

    是否有任何命令可以打印当前 jshell 会话中所有新创建的方法 类似的东西 list但仅适用于方法 您正在寻找命令 methods all 它会打印所有方法 包括启动 JShell 时添加的方法 以及失败 被覆盖或删除的方法 对于您声明的
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • Java Integer CompareTo() - 为什么使用比较与减法?

    我发现java lang Integer实施compareTo方法如下 public int compareTo Integer anotherInteger int thisVal this value int anotherVal an
  • 仅将 char[] 的一部分复制到 String 中

    我有一个数组 char ch 我的问题如下 如何将 ch 2 到 ch 7 的值合并到字符串中 我想在不循环 char 数组的情况下实现这一点 有什么建议么 感谢您花时间回答我的问题 Use new String value offset
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • Firebase 添加新节点

    如何将这些节点放入用户节点中 并创建另一个节点来存储帖子 我的数据库参考 databaseReference child user getUid setValue userInformations 您需要使用以下代码 databaseRef
  • 如何修复 JNLP 应用程序中的“缺少代码库、权限和应用程序名称清单属性”?

    随着最近的 Java 更新 许多人都遇到了缺少 Java Web Start 应用程序的问题Codebase Permissions and Application name体现属性 尽管有资源可以帮助您完成此任务 但我找不到任何资源综合的
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两

随机推荐