Add BodyInserters.fromValue(T, ParameterizedTypeReference<T>)
This commit introduces BodyInserters.fromValue(T, ParameterizedTypeReference<T>) variant as well as related WebClient.RequestBodySpec API, ServerResponse.BodyBuilder API and Kotlin extensions. Closes gh-32713
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -103,6 +103,32 @@ public abstract class BodyInserters {
|
||||
writeWithMessageWriters(message, context, Mono.just(body), ResolvableType.forInstance(body), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserter to write the given value.
|
||||
* <p>Alternatively, consider using the {@code bodyValue(Object, ParameterizedTypeReference)} shortcuts on
|
||||
* {@link org.springframework.web.reactive.function.client.WebClient WebClient} and
|
||||
* {@link org.springframework.web.reactive.function.server.ServerResponse ServerResponse}.
|
||||
* @param body the value to write
|
||||
* @param bodyType the type of the body, used to capture the generic type
|
||||
* @param <T> the type of the body
|
||||
* @return the inserter to write a single value
|
||||
* @throws IllegalArgumentException if {@code body} is a {@link Publisher} or an
|
||||
* instance of a type supported by {@link ReactiveAdapterRegistry#getSharedInstance()},
|
||||
* for which {@link #fromPublisher(Publisher, ParameterizedTypeReference)} or
|
||||
* {@link #fromProducer(Object, ParameterizedTypeReference)} should be used.
|
||||
* @since 6.2
|
||||
* @see #fromPublisher(Publisher, ParameterizedTypeReference)
|
||||
* @see #fromProducer(Object, ParameterizedTypeReference)
|
||||
*/
|
||||
public static <T> BodyInserter<T, ReactiveHttpOutputMessage> fromValue(T body, ParameterizedTypeReference<T> bodyType) {
|
||||
Assert.notNull(body, "'body' must not be null");
|
||||
Assert.notNull(bodyType, "'bodyType' must not be null");
|
||||
Assert.isNull(registry.getAdapter(body.getClass()),
|
||||
"'body' should be an object, for reactive types use a variant specifying a publisher/producer and its related element type");
|
||||
return (message, context) ->
|
||||
writeWithMessageWriters(message, context, Mono.just(body), ResolvableType.forType(bodyType), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserter to write the given object.
|
||||
* <p>Alternatively, consider using the {@code bodyValue(Object)} shortcuts on
|
||||
|
||||
@@ -367,6 +367,12 @@ final class DefaultWebClient implements WebClient {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> RequestHeadersSpec<?> bodyValue(T body, ParameterizedTypeReference<T> bodyType) {
|
||||
this.inserter = BodyInserters.fromValue(body, bodyType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, P extends Publisher<T>> RequestHeadersSpec<?> body(
|
||||
P publisher, ParameterizedTypeReference<T> elementTypeRef) {
|
||||
|
||||
@@ -692,9 +692,38 @@ public interface WebClient {
|
||||
* @throws IllegalArgumentException if {@code body} is a
|
||||
* {@link Publisher} or producer known to {@link ReactiveAdapterRegistry}
|
||||
* @since 5.2
|
||||
* @see #bodyValue(Object, ParameterizedTypeReference)
|
||||
*/
|
||||
RequestHeadersSpec<?> bodyValue(Object body);
|
||||
|
||||
/**
|
||||
* Shortcut for {@link #body(BodyInserter)} with a
|
||||
* {@linkplain BodyInserters#fromValue value inserter}.
|
||||
* For example:
|
||||
* <p><pre class="code">
|
||||
* List<Person> list = ... ;
|
||||
*
|
||||
* Mono<Void> result = client.post()
|
||||
* .uri("/persons/{id}", id)
|
||||
* .contentType(MediaType.APPLICATION_JSON)
|
||||
* .bodyValue(list, new ParameterizedTypeReference<List<Person>>() {};)
|
||||
* .retrieve()
|
||||
* .bodyToMono(Void.class);
|
||||
* </pre>
|
||||
* <p>For multipart requests consider providing
|
||||
* {@link org.springframework.util.MultiValueMap MultiValueMap} prepared
|
||||
* with {@link org.springframework.http.client.MultipartBodyBuilder
|
||||
* MultipartBodyBuilder}.
|
||||
* @param body the value to write to the request body
|
||||
* @param bodyType the type of the body, used to capture the generic type
|
||||
* @param <T> the type of the body
|
||||
* @return this builder
|
||||
* @throws IllegalArgumentException if {@code body} is a
|
||||
* {@link Publisher} or producer known to {@link ReactiveAdapterRegistry}
|
||||
* @since 6.2
|
||||
*/
|
||||
<T> RequestHeadersSpec<?> bodyValue(T body, ParameterizedTypeReference<T> bodyType);
|
||||
|
||||
/**
|
||||
* Shortcut for {@link #body(BodyInserter)} with a
|
||||
* {@linkplain BodyInserters#fromPublisher Publisher inserter}.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -225,6 +225,11 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder {
|
||||
return initBuilder(body, BodyInserters.fromValue(body));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<ServerResponse> bodyValue(T body, ParameterizedTypeReference<T> bodyType) {
|
||||
return initBuilder(body, BodyInserters.fromValue(body, bodyType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, P extends Publisher<T>> Mono<ServerResponse> body(P publisher, Class<T> elementClass) {
|
||||
return initBuilder(publisher, BodyInserters.fromPublisher(publisher, elementClass));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -420,6 +420,20 @@ public interface ServerResponse {
|
||||
*/
|
||||
Mono<ServerResponse> bodyValue(Object body);
|
||||
|
||||
/**
|
||||
* Set the body of the response to the given {@code Object} and return it.
|
||||
* This is a shortcut for using a {@link #body(BodyInserter)} with a
|
||||
* {@linkplain BodyInserters#fromValue value inserter}.
|
||||
* @param body the body of the response
|
||||
* @param bodyType the type of the body, used to capture the generic type
|
||||
* @param <T> the type of the body
|
||||
* @return the built response
|
||||
* @throws IllegalArgumentException if {@code body} is a
|
||||
* {@link Publisher} or producer known to {@link ReactiveAdapterRegistry}
|
||||
* @since 6.2
|
||||
*/
|
||||
<T> Mono<ServerResponse> bodyValue(T body, ParameterizedTypeReference<T> bodyType);
|
||||
|
||||
/**
|
||||
* Set the body from the given {@code Publisher}. Shortcut for
|
||||
* {@link #body(BodyInserter)} with a
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -68,6 +68,18 @@ inline fun <reified T : Any> RequestBodySpec.body(flow: Flow<T>): RequestHeaders
|
||||
inline fun <reified T : Any> RequestBodySpec.body(producer: Any): RequestHeadersSpec<*> =
|
||||
body(producer, object : ParameterizedTypeReference<T>() {})
|
||||
|
||||
/**
|
||||
* Extension for [WebClient.RequestBodySpec.bodyValue] providing a `bodyValueWithType<T>(Any)` variant
|
||||
* leveraging Kotlin reified type parameters. This extension is not subject to type
|
||||
* erasure and retains actual generic type arguments.
|
||||
* @param body the value to write to the request body
|
||||
* @param T the type of the body
|
||||
* @author Sebastien Deleuze
|
||||
* @since 6.2
|
||||
*/
|
||||
inline fun <reified T : Any> RequestBodySpec.bodyValueWithType(body: T): RequestHeadersSpec<*> =
|
||||
bodyValue(body, object : ParameterizedTypeReference<T>() {})
|
||||
|
||||
/**
|
||||
* Coroutines variant of [WebClient.RequestHeadersSpec.exchange].
|
||||
*
|
||||
|
||||
@@ -51,9 +51,6 @@ inline fun <reified T : Any> ServerResponse.BodyBuilder.body(producer: Any): Mon
|
||||
/**
|
||||
* Coroutines variant of [ServerResponse.BodyBuilder.bodyValue].
|
||||
*
|
||||
* Set the body of the response to the given {@code Object} and return it.
|
||||
* This convenience method combines [body] and
|
||||
* [org.springframework.web.reactive.function.BodyInserters.fromValue].
|
||||
* @param body the body of the response
|
||||
* @return the built response
|
||||
* @throws IllegalArgumentException if `body` is a [Publisher] or an
|
||||
@@ -62,6 +59,21 @@ inline fun <reified T : Any> ServerResponse.BodyBuilder.body(producer: Any): Mon
|
||||
suspend fun ServerResponse.BodyBuilder.bodyValueAndAwait(body: Any): ServerResponse =
|
||||
bodyValue(body).awaitSingle()
|
||||
|
||||
/**
|
||||
* Coroutines variant of [ServerResponse.BodyBuilder.bodyValue] providing a `bodyValueWithTypeAndAwait<T>(Any)` variant
|
||||
* leveraging Kotlin reified type parameters. This extension is not subject to type
|
||||
* erasure and retains actual generic type arguments.
|
||||
*
|
||||
* @param body the body of the response
|
||||
* @param T the type of the body
|
||||
* @return the built response
|
||||
* @throws IllegalArgumentException if `body` is a [Publisher] or an
|
||||
* instance of a type supported by [org.springframework.core.ReactiveAdapterRegistry.getSharedInstance],
|
||||
* @since 6.2
|
||||
*/
|
||||
suspend inline fun <reified T: Any> ServerResponse.BodyBuilder.bodyValueWithTypeAndAwait(body: T): ServerResponse =
|
||||
bodyValue(body, object : ParameterizedTypeReference<T>() {}).awaitSingle()
|
||||
|
||||
/**
|
||||
* Coroutines variant of [ServerResponse.BodyBuilder.body] with [Any] and
|
||||
* [ParameterizedTypeReference] parameters providing a `bodyAndAwait(Flow<T>)` variant.
|
||||
|
||||
Reference in New Issue
Block a user