Suppose I have the following application with a WebFilter
and I'm trying to pass some context back from the controller into the filter.
@RestController
class MyController {
@PostMapping("/test")
suspend fun postSomething(): ResponseEntity<Unit> {
val valueFromFilter = coroutineContext[ReactorContext.Key]?.context?.get<String>("myKey") ?: "EMPTY"
logger.info { "Inside handler = $valueFromFilter" } // this works since Reactor populate coroutineContext with respective ReactorContext
coroutineContext[ReactorContext.Key]?.context?.put("handlerKey", "hello")
return ResponseEntity.ok().build()
}
}
@Component
class MyFilter : WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
logger.info { "Before" }
return chain.filter(exchange)
.contextWrite(Context.of("myKey", "myValue"))
.doOnEach {
val valueFromHandler = if (it.contextView.hasKey("handlerKey")) it.contextView.get<String>("handlerKey") else "EMPTY"
logger.info { "After handler = $valueFromHandler" } // But this doesn't work since Reactor doesn't restore ReactorContext from respective coroutineContext
}
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
run(MyApplication::class.java, *args)
}
In this example if we made a request to /test
it would print the following
Before
Inside handler = myValue
After handler = EMPTY
I understand that when we call chain.filter(exchange)
to proceed with the request and the handler is a Kotlin suspend
function the framework fills in coroutineContext
with ReactorContext.Key to actualReactorContext
. Although my question is why doesn't Spring restore the context that I may have filled in the controller back, so I can use it after calling chain.filter(exchange)
. Moreover if there is anyway possible to do this currently.
Just figured out I was looking into the wrong way of doing it. Instead one can simply just use the attributes of
ServerWebExchange
to pass context around likeThis way we can pass context between both the filter and the handler.