为什么在调用 .block() 时反应器上下文抛出 "Context does not contain key"?

Why is reactor context throwing "Context does not contain key" when .block() is called?

我有一个 WebFilter 注入上下文

public class MyWebFilter implements WebFilter {
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    return chain.filter(exchange)
      .contextWrite(ctx -> ctx.put("context", "some-context"));
  }
}

在我的控制器中,我可以读取它并且return它成功

@RestController
public class MyController {
  @GetMapping(path = "test")
  public Mono<String> test() {
    return Mono.deferContextual(ctx -> Mono.just(ctx.get("context")));
  }
}

但是,如果我在上面调用 .block()

Mono.deferContextual(ctx -> Mono.just(ctx.get("context"))).block();

它抛出这个异常

java.util.NoSuchElementException: Context does not contain key: context

我无法理解反应世界中的执行顺序。

您会希望 Spring 尽可能 'realistically' 处理此问题。创建一个测试并使用 WebTestClient。 (不要调用 .block() ever 否则你会拒绝服务你的服务器 w/a 半打用户。不要 inject/call 你的控制器方法直接,你需要所有的代理、过滤器等)

这是科特林,但你应该能够理解它:

@SpringBootApplication
class WebfluxScratchpadApplication {
    @Bean
    fun filter(): WebFilter = WebFilter { serverWebExchange, webFilterChain ->
        webFilterChain.filter(serverWebExchange)
            .contextWrite {
                it.put("myContextKey", "!!")
            }
    }
}

@RestController
class MyController {
    @GetMapping("/foo")
    fun foo() = Mono.just("foo")
        .flatMap { s ->
            Mono.deferContextual { ctx ->
                Mono.just(String.format("%s%s", s, ctx["myContextKey"]))
            }
        }
}

fun main(args: Array<String>) {
    runApplication<WebfluxScratchpadApplication>(*args)
}

@WebFluxTest
class WebfluxScratchpadApplicationTests {

    @Autowired
    lateinit var client: WebTestClient

    @Test
    fun stuff() {
        Assertions.assertEquals(
            "foo!!",
            client.get()
                .uri("/foo")
                .exchange()
                .expectBody<String>()
                .returnResult()
                .responseBody!!
        )
    }
}


当您调用 .block() 时,该行会立即被订阅和评估,与外链中提供的上下文没有任何联系。当你 return 没有 .block 时,它会成为具有上下文的更大链的一部分,并且不会被评估,直到某些东西订阅了外部链,此时上下文可用。