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 的相关文章

  • ElasticBeanstalk Java,Spring 活动配置文件

    我正在尝试通过 AWS ElasticBeanstalk 启动 spring boot jar 一切正常 配置文件为 默认 有谁知道如何为 java ElasticBeanstalk 应用程序 不是 tomcat 设置活动配置文件 spri
  • 解决错误:日志已在具有多个实例的atomikos中使用

    我仅在使用atomikos的实时服务器上遇到问题 在我的本地服务器上它工作得很好 我在服务器上面临的问题是 init 中出错 日志已在使用中 完整的异常堆栈跟踪 java lang RuntimeException Log already
  • manifest.mf 文件的附加内容的约定?

    Java JAR 中的 MANIFEST MF 文件是否有任何超出 MANIFEST MF 约定的约定 JAR规范 http download oracle com javase 1 4 2 docs guide jar jar html
  • 如何将 Flux 包装在 ResponseEntity 中

    我需要我的端点以以下 json 格式返回数据 code SUCCESS message SUCCESS errors null data 这是我的控制器代码 GetMapping value productSubcategories pro
  • 如何查找 Android 设备中的所有文件并将它们放入列表中?

    我正在寻求帮助来列出 Android 外部存储设备中的所有文件 我想查找所有文件夹 包括主文件夹的子文件夹 有办法吗 我已经做了一个基本的工作 但我仍然没有得到想要的结果 这不起作用 这是我的代码 File files array file
  • java中删除字符串中的特殊字符?

    如何删除字符串中除 之外的特殊字符 现在我用 replaceAll w s 它删除了所有特殊字符 但我想保留 谁能告诉我我该怎么办 Use replaceAll w s 我所做的是将下划线和连字符添加到正则表达式中 我添加了一个 连字符之前
  • jdbc4.MySQLSyntaxErrorException:数据库中不存在表

    我正在使用 SpringBoot 开发一个网络应用程序 这是我的application properties文件来指定访问数据库的凭据 spring datasource driverClassName com mysql jdbc Dri
  • hibernate总是自己删除表中的所有数据

    您好 我正在开发一个 spring mvc 应用程序 它使用 hibernate 连接到存储文件的 mysql 数据库 我有两个方法 一个方法添加我选择的特定文件路径中的所有文件 另一种方法调用查询以返回从 mysql 存储的文件列表 问题
  • Prim 的迷宫生成算法:获取相邻单元格

    我基于 Prim 算法编写了一个迷宫生成器程序 该算法是 Prim 算法的随机版本 从充满墙壁的网格开始 选择一个单元格 将其标记为迷宫的一部分 将单元格的墙壁添加到墙壁列表中 While there are walls in the li
  • 迁移到 java 17 后有关“每个进程的内存映射”和 JVM 崩溃的 GC 警告

    我们正在将 java 8 应用程序迁移到 java 17 并将 GC 从G1GC to ZGC 我们的应用程序作为容器运行 这两个基础映像之间的唯一区别是 java 的版本 例如对于 java 17 版本 FROM ubuntu 20 04
  • 如何将文件透明地传输到浏览器?

    受控环境 IE8 IIS 7 ColdFusion 当从 IE 发出指向媒体文件 例如 mp3 mpeg 等 的 GET 请求时 浏览器将启动关联的应用程序 Window Media Player 我猜测 IIS 提供文件的方式允许应用程序
  • 反思 Groovy 脚本中声明的函数

    有没有一种方法可以获取 Groovy 脚本中声明的函数的反射数据 该脚本已通过GroovyShell目的 具体来说 我想枚举脚本中的函数并访问附加到它们的注释 Put this到 Groovy 脚本的最后一行 它将作为脚本的返回值 a la
  • Tomcat 6找不到mysql驱动

    这里有一个类似的问题 但关于类路径 ClassNotFoundException com mysql jdbc Driver https stackoverflow com questions 1585811 classnotfoundex
  • 将 JSON 参数从 java 发布到 sinatra 服务

    我有一个 Android 应用程序发布到我的 sinatra 服务 早些时候 我无法读取 sinatra 服务上的参数 但是 在我将内容类型设置为 x www form urlencoded 之后 我能够看到参数 但不完全是我想要的 我在
  • 当单元格内的 JComboBox 中有 ItemEvent 时,如何获取 CellRow

    我有一个 JTable 其中有一列包含 JComboBox 我有一个附加到 JComboBox 的 ItemListener 它会根据任何更改进行操作 但是 ItemListener 没有获取更改的 ComboBox 所在行的方法 当组合框
  • Android JNI C 简单追加函数

    我想制作一个简单的函数 返回两个字符串的值 基本上 java public native String getAppendedString String name c jstring Java com example hellojni He
  • 将 JTextArea 内容写入文件

    我在 Java Swing 中有一个 JTextArea 和一个 提交 按钮 需要将textarea的内容写入一个带有换行符的文件中 我得到的输出是这样的 它被写为文件中的一个字符串 try BufferedWriter fileOut n
  • 将2-3-4树转换为红黑树

    我正在尝试将 2 3 4 树转换为 java 中的红黑树 但我无法弄清楚它 我将这两个基本类编写如下 以使问题简单明了 但不知道从这里到哪里去 public class TwoThreeFour
  • java迭代器内部是如何工作的? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一个员工列表 List
  • 中断连接套接字

    我有一个 GUI 其中包含要连接的服务器列表 如果用户单击服务器 则会连接到该服务器 如果用户单击第二个服务器 它将断开第一个服务器的连接并连接到第二个服务器 每个新连接都在一个新线程中运行 以便程序可以执行其他任务 但是 如果用户在第一个

随机推荐