我正在使用春天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(使用前将#替换为@)