@@ -20,24 +20,26 @@ import kotlinx.coroutines.Job
2020import kotlinx.coroutines.currentCoroutineContext
2121import kotlinx.coroutines.flow.Flow
2222import kotlinx.coroutines.reactive.asFlow
23- import kotlinx.coroutines.reactor.asFlux
24- import kotlinx.coroutines.reactor.awaitSingle
25- import kotlinx.coroutines.reactor.awaitSingleOrNull
26- import kotlinx.coroutines.reactor.mono
23+ import kotlinx.coroutines.reactor.*
24+ import kotlinx.coroutines.withContext
2725import org.reactivestreams.Publisher
2826import org.springframework.core.ParameterizedTypeReference
2927import org.springframework.http.ResponseEntity
28+ import org.springframework.web.reactive.function.client.CoExchangeFilterFunction.Companion.COROUTINE_CONTEXT_ATTRIBUTE
3029import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec
3130import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec
3231import reactor.core.publisher.Flux
3332import reactor.core.publisher.Mono
33+ import reactor.util.context.Context
34+ import kotlin.coroutines.CoroutineContext
3435
3536/* *
3637 * Extension for [WebClient.RequestBodySpec.body] providing a `body(Publisher<T>)` variant
3738 * leveraging Kotlin reified type parameters. This extension is not subject to type
3839 * erasure and retains actual generic type arguments.
3940 *
4041 * @author Sebastien Deleuze
42+ * @author Dmitry Sulman
4143 * @since 5.0
4244 */
4345inline fun <reified T : Any , S : Publisher <T >> RequestBodySpec.body (publisher : S ): RequestHeadersSpec <* > =
@@ -89,7 +91,7 @@ inline fun <reified T : Any> RequestBodySpec.bodyValueWithType(body: T): Request
8991 */
9092suspend fun <T : Any > RequestHeadersSpec <out RequestHeadersSpec <* >>.awaitExchange (responseHandler : suspend (ClientResponse ) -> T ): T {
9193 val context = currentCoroutineContext().minusKey(Job .Key )
92- return exchangeToMono { mono(context) { responseHandler.invoke(it) } }.awaitSingle()
94+ return withContext(context.toReactorContext()) { exchangeToMono { mono(context) { responseHandler.invoke(it) } }.awaitSingle() }
9395}
9496
9597/* *
@@ -99,7 +101,7 @@ suspend fun <T: Any> RequestHeadersSpec<out RequestHeadersSpec<*>>.awaitExchange
99101 */
100102suspend fun <T : Any > RequestHeadersSpec <out RequestHeadersSpec <* >>.awaitExchangeOrNull (responseHandler : suspend (ClientResponse ) -> T ? ): T ? {
101103 val context = currentCoroutineContext().minusKey(Job .Key )
102- return exchangeToMono { mono(context) { responseHandler.invoke(it) } }.awaitSingleOrNull()
104+ return withContext(context.toReactorContext()) { exchangeToMono { mono(context) { responseHandler.invoke(it) } }.awaitSingleOrNull() }
103105}
104106
105107/* *
@@ -150,29 +152,39 @@ inline fun <reified T : Any> WebClient.ResponseSpec.bodyToFlow(): Flow<T> =
150152 * @author Sebastien Deleuze
151153 * @since 5.2
152154 */
153- suspend inline fun <reified T : Any > WebClient.ResponseSpec.awaitBody () : T =
154- when (T ::class ) {
155- Unit ::class -> awaitBodilessEntity().let { Unit as T }
156- else -> bodyToMono<T >().awaitSingle()
155+ suspend inline fun <reified T : Any > WebClient.ResponseSpec.awaitBody () : T {
156+ val context = currentCoroutineContext().minusKey(Job .Key )
157+ return withContext(context.toReactorContext()) {
158+ when (T ::class ) {
159+ Unit ::class -> toBodilessEntity().awaitSingle().let { Unit as T }
160+ else -> bodyToMono<T >().awaitSingle()
161+ }
157162 }
163+ }
158164
159165/* *
160166 * Coroutines variant of [WebClient.ResponseSpec.bodyToMono].
161167 *
162168 * @author Valentin Shakhov
163169 * @since 5.3.6
164170 */
165- suspend inline fun <reified T : Any > WebClient.ResponseSpec.awaitBodyOrNull () : T ? =
166- when (T ::class ) {
167- Unit ::class -> awaitBodilessEntity().let { Unit as T ? }
168- else -> bodyToMono<T >().awaitSingleOrNull()
171+ suspend inline fun <reified T : Any > WebClient.ResponseSpec.awaitBodyOrNull () : T ? {
172+ val context = currentCoroutineContext().minusKey(Job .Key )
173+ return withContext(context.toReactorContext()) {
174+ when (T ::class ) {
175+ Unit ::class -> toBodilessEntity().awaitSingle().let { Unit as T ? }
176+ else -> bodyToMono<T >().awaitSingleOrNull()
177+ }
169178 }
179+ }
170180
171181/* *
172182 * Coroutines variant of [WebClient.ResponseSpec.toBodilessEntity].
173183 */
174- suspend fun WebClient.ResponseSpec.awaitBodilessEntity () =
175- toBodilessEntity().awaitSingle()
184+ suspend fun WebClient.ResponseSpec.awaitBodilessEntity (): ResponseEntity <Void > {
185+ val context = currentCoroutineContext().minusKey(Job .Key )
186+ return withContext(context.toReactorContext()) { toBodilessEntity().awaitSingle() }
187+ }
176188
177189/* *
178190 * Extension for [WebClient.ResponseSpec.toEntity] providing a `toEntity<Foo>()` variant
@@ -203,3 +215,22 @@ inline fun <reified T : Any> WebClient.ResponseSpec.toEntityList(): Mono<Respons
203215 */
204216inline fun <reified T : Any > WebClient.ResponseSpec.toEntityFlux (): Mono <ResponseEntity <Flux <T >>> =
205217 toEntityFlux(object : ParameterizedTypeReference <T >() {})
218+
219+ /* *
220+ * Extension for [WebClient.ResponseSpec.toEntity] providing a `toEntity<Foo>()` variant
221+ * leveraging Kotlin reified type parameters and allows [kotlin.coroutines.CoroutineContext]
222+ * propagation to the [CoExchangeFilterFunction]. This extension is not subject to type erasure
223+ * and retains actual generic type arguments.
224+ *
225+ * @since 7.0.0
226+ */
227+ suspend inline fun <reified T : Any > WebClient.ResponseSpec.awaitEntity (): ResponseEntity <T > {
228+ val context = currentCoroutineContext().minusKey(Job .Key )
229+ return withContext(context.toReactorContext()) { toEntity(T ::class .java).awaitSingle() }
230+ }
231+
232+ @PublishedApi
233+ internal fun CoroutineContext.toReactorContext (): ReactorContext {
234+ val context = Context .of(COROUTINE_CONTEXT_ATTRIBUTE , this ).readOnly()
235+ return (this [ReactorContext .Key ]?.context?.putAll(context) ? : context).asCoroutineContext()
236+ }
0 commit comments