Spring RequestContextHolder 和 WebTestClient [英] Spring RequestContextHolder and WebTestClient

查看:21
本文介绍了Spring RequestContextHolder 和 WebTestClient的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在控制器中使用 Spring RequestContextHolder 并且它工作正常.但是在单元测试中,我使用 WebTestClient 得到了 java.lang.IllegalStateException.下面是一个例子:

package demo.reactive.controller;导入 org.springframework.http.ResponseEntity;导入 org.springframework.web.bind.annotation.GetMapping;导入 org.springframework.web.bind.annotation.RestController;导入 org.springframework.web.context.request.RequestContextHolder;进口reactor.core.publisher.Mono;@RestController公共类 FooController {@GetMapping("/foo")公共 ResponseEntity>富(){String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();返回 ResponseEntity.ok(Mono.just(sessionId));}}

package demo.reactive;导入 org.junit.jupiter.api.Test;导入 org.springframework.beans.factory.annotation.Autowired;导入 org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;导入 org.springframework.test.web.reactive.server.WebTestClient;导入 demo.reactive.controller.FooController;@WebFluxTest(FooController.class)类 DemoReactiveApplicationTests {@Autowired 私有 WebTestClient 客户端;@测试无效上下文加载(){client.get().uri("/foo").exchange().expectStatus().isOk();}}

<块引用>

java.lang.IllegalStateException: No thread-bound request found: 您是指实际 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(Native Method) ~[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 架构.

话虽如此,我们可以使用 reactor 的 Mono#subscriberContext() 运算符来实现替代方案.创建可重用解决方案的最佳方法是实现 spring WebFilter 以获取请求属性并将其设置在反应器上下文中.

public class WebRequestAttributesContextFilter 实现 WebFilter {public static final String WEB_REQUEST_ATTRIBUTES = "WebRequestAttributes";@覆盖公共 Mono过滤器(ServerWebExchange serverWebExchange,WebFilterChain webFilterChain){返回 webFilterChain.filter(serverWebExchange).subscriberContext(context -> context.put(WEB_REQUEST_ATTRIBUTES, serverWebExchange.getAttributes()));}}

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

@GetMapping("/foo")公共单声道你好世界(){return Mono.subscriberContext().map(context -> (String) context.>get(WebRequestAttributesContextFilter.WEB_REQUEST_ATTRIBUTES).get("会话"));}

I'm using Spring RequestContextHolder in the controller and it works fine. But in the unit test I get java.lang.IllegalStateException using WebTestClient. Here is an example:

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: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE] Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s): |_ checkpoint ⇢ HTTP GET "/foo" [ExceptionHandlingWebHandler] Stack trace: at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE] at demo.reactive.controller.FooController.foo(FooController.java:15) ~[classes/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:147) ~[spring-webflux-5.2.5.RELEASE.jar:5.2.5.RELEASE] at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:151) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:48) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:153) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:274) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:851) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:173) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2274) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:132) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2082) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1956) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:145) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:4210) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:441) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:211) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:4210) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at org.springframework.test.web.reactive.server.HttpHandlerConnector.lambda$doConnect$1(HttpHandlerConnector.java:97) ~[spring-test-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.mock.http.client.reactive.MockClientHttpRequest.lambda$null$2(MockClientHttpRequest.java:121) ~[spring-test-5.2.5.RELEASE.jar:5.2.5.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:4210) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxConcatIterable$ConcatIterableSubscriber.onComplete(FluxConcatIterable.java:146) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.FluxConcatIterable.subscribe(FluxConcatIterable.java:60) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoFromFluxOperator.subscribe(MonoFromFluxOperator.java:72) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at org.springframework.test.web.reactive.server.HttpHandlerConnector.doConnect(HttpHandlerConnector.java:108) ~[spring-test-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.test.web.reactive.server.HttpHandlerConnector.lambda$connect$0(HttpHandlerConnector.java:79) ~[spring-test-5.2.5.RELEASE.jar:5.2.5.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:4210) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.run(MonoSubscribeOn.java:124) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37) ~[reactor-core-3.3.4.RELEASE.jar:3.3.4.RELEASE] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na] at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na] at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

How can I get the unit test to work. Or it's not even suppose to use RequestContextHolder with WebFlux?

解决方案

Spring RequestContextHolder cannot be used with Spring Webflux. RequestContextHolder primary works using java.lang.ThreadLocal. This clearly doesn't fit with Reactor architecture.

Having said that, We can implement an alternative using reactor's Mono#subscriberContext() operator. The best way to create a reusable solution is to implement spring WebFilter to get hold of request attributes and set it in the reactor context.

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

The attributes (sessionId in your case) can then be read from the reactor context anywhere in the request processing pipeline as follows:

@GetMapping("/foo")
public Mono<String> helloWorld() {
  return Mono.subscriberContext().map(context -> (String) context.<Map<String, Object>>get(
          WebRequestAttributesContextFilter.WEB_REQUEST_ATTRIBUTES
      ).get("session"));
}

这篇关于Spring RequestContextHolder 和 WebTestClient的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆