@@ -20,24 +20,26 @@ import kotlinx.coroutines.Job
20
20
import kotlinx.coroutines.currentCoroutineContext
21
21
import kotlinx.coroutines.flow.Flow
22
22
import 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
27
25
import org.reactivestreams.Publisher
28
26
import org.springframework.core.ParameterizedTypeReference
29
27
import org.springframework.http.ResponseEntity
28
+ import org.springframework.web.reactive.function.client.CoExchangeFilterFunction.Companion.COROUTINE_CONTEXT_ATTRIBUTE
30
29
import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec
31
30
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec
32
31
import reactor.core.publisher.Flux
33
32
import reactor.core.publisher.Mono
33
+ import reactor.util.context.Context
34
+ import kotlin.coroutines.CoroutineContext
34
35
35
36
/* *
36
37
* Extension for [WebClient.RequestBodySpec.body] providing a `body(Publisher<T>)` variant
37
38
* leveraging Kotlin reified type parameters. This extension is not subject to type
38
39
* erasure and retains actual generic type arguments.
39
40
*
40
41
* @author Sebastien Deleuze
42
+ * @author Dmitry Sulman
41
43
* @since 5.0
42
44
*/
43
45
inline 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
89
91
*/
90
92
suspend fun <T : Any > RequestHeadersSpec <out RequestHeadersSpec <* >>.awaitExchange (responseHandler : suspend (ClientResponse ) -> T ): T {
91
93
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() }
93
95
}
94
96
95
97
/* *
@@ -99,7 +101,7 @@ suspend fun <T: Any> RequestHeadersSpec<out RequestHeadersSpec<*>>.awaitExchange
99
101
*/
100
102
suspend fun <T : Any > RequestHeadersSpec <out RequestHeadersSpec <* >>.awaitExchangeOrNull (responseHandler : suspend (ClientResponse ) -> T ? ): T ? {
101
103
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() }
103
105
}
104
106
105
107
/* *
@@ -150,29 +152,39 @@ inline fun <reified T : Any> WebClient.ResponseSpec.bodyToFlow(): Flow<T> =
150
152
* @author Sebastien Deleuze
151
153
* @since 5.2
152
154
*/
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
+ }
157
162
}
163
+ }
158
164
159
165
/* *
160
166
* Coroutines variant of [WebClient.ResponseSpec.bodyToMono].
161
167
*
162
168
* @author Valentin Shakhov
163
169
* @since 5.3.6
164
170
*/
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
+ }
169
178
}
179
+ }
170
180
171
181
/* *
172
182
* Coroutines variant of [WebClient.ResponseSpec.toBodilessEntity].
173
183
*/
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
+ }
176
188
177
189
/* *
178
190
* Extension for [WebClient.ResponseSpec.toEntity] providing a `toEntity<Foo>()` variant
@@ -203,3 +215,22 @@ inline fun <reified T : Any> WebClient.ResponseSpec.toEntityList(): Mono<Respons
203
215
*/
204
216
inline fun <reified T : Any > WebClient.ResponseSpec.toEntityFlux (): Mono <ResponseEntity <Flux <T >>> =
205
217
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