当前位置: 首页 > 知识库问答 >
问题:

Spring RequestContextHolder和WebTestClient

邹海荣
2023-03-14

我在控制器中使用了SpringRequestContextHolder,它工作得很好。但是在单元测试中,我使用WebTestClient获得java.lang.IllegalStateException。这里有一个例子:

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

如何使单元测试工作。或者甚至不需要在WebFlux中使用RequestContextHolder

共有1个答案

严项明
2023-03-14

SpringRequestContextHolder不能与Spring WebFlux一起使用。RequestContextHolderprimary使用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()));
  }
}

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

@GetMapping("/foo")
public Mono<String> helloWorld() {
  return Mono.subscriberContext().map(context -> (String) context.<Map<String, Object>>get(
          WebRequestAttributesContextFilter.WEB_REQUEST_ATTRIBUTES
      ).get("session"));
}
 类似资料:
  • 问题内容: 关于它们有很多传说。我想知道真相。以下两个示例之间有什么区别? 问题答案: 不确定从何处获得传说,但: 提交按钮 与: IE6将在标记之间提交此按钮的所有文本,其他浏览器将仅提交值。使用可使您在按钮的设计上享有更大的布局自由度。从各种意图和目的看,它乍一看似乎很棒,但是各种浏览器怪癖使它有时很难使用。 在您的示例中,IE6将发送到服务器,而其他大多数浏览器将不发送任何内容。要使其跨浏览

  • 什么区别以及如何正确重写代码?

  • 我试图理解为什么下面两个代码块会产生不同的结果。 代码块1按预期工作,并返回从数据库中查找的提供程序的数组。另一方面,代码块2返回函数数组。在理解promissione.all()和async/await时,我觉得缺少了一些简单的东西。 代码块的差异如下: > 块1:创建许诺函数数组,然后使用map运算符将其包装在异步函数中。 块2:许诺函数的数组被创建为异步函数。因此,不调用map运算符。 如果

  • 问题内容: 我才刚刚开始研究SQL。 我有一个SQL Server 2008r2数据库,它将返回两个字段DocDate和InvValue。我需要将InvValues汇总为今天的MTD和YTD,所以看起来像 我已经做了大量的Google搜寻,并且可以使用SUM&DATEPART进行一项或多项,但是我坚持尝试两者兼而有之。 有人可以给我一些伪代码,以帮助我进一步谷歌。 谢谢@戈登·利诺夫(Gordon

  • 这个示例代码来自一本Java书籍,从墙上的99瓶啤酒到没有啤酒打印出这首歌。问题是,当墙上是1瓶啤酒时,它仍然写着瓶子。我试图通过在末尾添加部分来解决这个问题。但是,它仍然显示1瓶啤酒在墙上,我瓶啤酒在墙上。 我不知道该改变什么来解决这个问题。是否创建另一个while部分? 如果你能给他们一个提示,这样我就可以自己解决它,那也很酷!因为我知道我的实际歌曲输出是在第一个if部分,但我不知道我应该在哪

  • 原文地址:https://cesiumjs.org/tutorials/cesium-and-webpack/ Cesium 和 Webpack Webpack是非常强大非常流行的JavaScript 模块打包工具。它可以让开发人员以一种简单直观的 require 方式去加载各种页面需要的文件,极大的方便了开源人员对代码和资源文件进行结构化设计。当编译的时候,它会跟踪代码依赖性,把所有的模型打包到