1

I have a WebFilter that injects a context

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

In my Controller, I can read it and return it successfully

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

However, if I call .block() on it:

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

it throws this exception

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

I'm having trouble wrapping my head around the order of execution in the reactive world.

3
  • why would you call block in a non-blocking framework? Commented Mar 19, 2021 at 9:59
  • Wtih block you are breaking the reactive pipeline/chain and the context can't be propagated that way. Context is propgated through subscription. Commented Mar 19, 2021 at 21:41
  • Why is the .block() method available and when is it allowed then? Commented Mar 19, 2021 at 23:51

2 Answers 2

6

When you call .block(), that line is subscribed to and evaluated immediately, without any connection to the context provided in the outer chain. When you return without .block, it becomes part of the larger chain which has the context, and isn't evaluated until something subscribes to the outer chain at which time the context is available.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. Now that I understand reactive a lot better, this makes sense
2

You're going to want Spring to handle this as 'realistically' as possible. Create a test and use the WebTestClient. (Don't call .block() ever or you'll DoS your server w/a half dozen users. Don't inject/call your controller methods directly, you need all the proxies, filters, etc.)

This is kotlin but you should be able to figure it out:

@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!!
        )
    }
}


1 Comment

Your soution not work at me. I have same problem. I will open a new ticket next day and link to here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.