Polish MultipartBodyBuilder
Improve Javadoc Consistently reject Publisher unless using asyncPart Consistently set Content-Type when specified
This commit is contained in:
parent
6eac141160
commit
49e5c4dcf6
|
@ -36,19 +36,39 @@ import org.springframework.util.LinkedMultiValueMap;
|
|||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* A mutable builder for multipart form bodies. For example:
|
||||
* Builder for the body of a multipart request, producing
|
||||
* {@code MultiValueMap<String, HttpEntity>}, which can be provided to the
|
||||
* {@code WebClient} through the {@code syncBody} method.
|
||||
*
|
||||
* Examples:
|
||||
* <pre class="code">
|
||||
*
|
||||
* // Add form field
|
||||
* MultipartBodyBuilder builder = new MultipartBodyBuilder();
|
||||
* builder.part("form field", "form value");
|
||||
* builder.part("form field", "form value").header("foo", "bar");
|
||||
*
|
||||
* // Add file part
|
||||
* Resource image = new ClassPathResource("image.jpg");
|
||||
* builder.part("image", image).header("Baz", "Qux");
|
||||
* builder.part("image", image).header("foo", "bar");
|
||||
*
|
||||
* // Add content (e.g. JSON)
|
||||
* Account account = ...
|
||||
* builder.part("account", account).header("foo", "bar");
|
||||
*
|
||||
* // Add content from Publisher
|
||||
* Mono<Account> accountMono = ...
|
||||
* builder.asyncPart("account", accountMono).header("foo", "bar");
|
||||
*
|
||||
* // Build and use
|
||||
* MultiValueMap<String, HttpEntity<?>> multipartBody = builder.build();
|
||||
* // use multipartBody with RestTemplate or WebClient
|
||||
*
|
||||
* Mono<Void> result = webClient.post()
|
||||
* .uri("...")
|
||||
* .syncBody(multipartBody)
|
||||
* .retrieve()
|
||||
* .bodyToMono(Void.class)
|
||||
* </pre>
|
||||
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0.2
|
||||
|
@ -67,7 +87,14 @@ public final class MultipartBodyBuilder {
|
|||
|
||||
|
||||
/**
|
||||
* Add a part from an Object.
|
||||
* Add a part where the Object may be:
|
||||
* <ul>
|
||||
* <li>String -- form field
|
||||
* <li>{@link org.springframework.core.io.Resource Resource} -- file part
|
||||
* <li>Object -- content to be encoded (e.g. to JSON)
|
||||
* <li>HttpEntity -- part content and headers although generally it's
|
||||
* easier to add headers through the returned builder</li>
|
||||
* </ul>
|
||||
* @param name the name of the part to add
|
||||
* @param part the part data
|
||||
* @return builder that allows for further customization of part headers
|
||||
|
@ -77,51 +104,53 @@ public final class MultipartBodyBuilder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #part(String, Object)} that also accepts a MediaType
|
||||
* which is used to determine how to encode the part.
|
||||
* Variant of {@link #part(String, Object)} that also accepts a MediaType.
|
||||
* @param name the name of the part to add
|
||||
* @param part the part data
|
||||
* @param contentType the media type for the part
|
||||
* @param contentType the media type to help with encoding the part
|
||||
* @return builder that allows for further customization of part headers
|
||||
*/
|
||||
public PartBuilder part(String name, Object part, @Nullable MediaType contentType) {
|
||||
Assert.hasLength(name, "'name' must not be empty");
|
||||
Assert.notNull(part, "'part' must not be null");
|
||||
|
||||
if (part instanceof Publisher) {
|
||||
throw new IllegalArgumentException("Use publisher(String, Publisher, Class) or " +
|
||||
"publisher(String, Publisher, ParameterizedTypeReference) for adding Publisher parts");
|
||||
}
|
||||
|
||||
if (part instanceof PublisherEntity<?,?>) {
|
||||
PublisherPartBuilder<?, ?> builder = new PublisherPartBuilder<>((PublisherEntity<?, ?>) part);
|
||||
if (contentType != null) {
|
||||
builder.header(HttpHeaders.CONTENT_TYPE, contentType.toString());
|
||||
}
|
||||
this.parts.add(name, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
Object partBody;
|
||||
HttpHeaders partHeaders = new HttpHeaders();
|
||||
|
||||
HttpHeaders partHeaders = null;
|
||||
if (part instanceof HttpEntity) {
|
||||
HttpEntity<?> httpEntity = (HttpEntity<?>) part;
|
||||
partBody = httpEntity.getBody();
|
||||
partHeaders.addAll(httpEntity.getHeaders());
|
||||
partBody = ((HttpEntity<?>) part).getBody();
|
||||
partHeaders = new HttpHeaders();
|
||||
partHeaders.putAll(((HttpEntity<?>) part).getHeaders());
|
||||
}
|
||||
else {
|
||||
partBody = part;
|
||||
}
|
||||
|
||||
if (contentType != null) {
|
||||
partHeaders.setContentType(contentType);
|
||||
if (partBody instanceof Publisher) {
|
||||
throw new IllegalArgumentException(
|
||||
"Use asyncPart(String, Publisher, Class)" +
|
||||
" or asyncPart(String, Publisher, ParameterizedTypeReference) or" +
|
||||
" or MultipartBodyBuilder.PublisherEntity");
|
||||
}
|
||||
|
||||
DefaultPartBuilder builder = new DefaultPartBuilder(partHeaders, partBody);
|
||||
if (contentType != null) {
|
||||
builder.header(HttpHeaders.CONTENT_TYPE, contentType.toString());
|
||||
}
|
||||
this.parts.add(name, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an asynchronous part with {@link Publisher}-based content.
|
||||
* Add a part from {@link Publisher} content.
|
||||
* @param name the name of the part to add
|
||||
* @param publisher the part contents
|
||||
* @param elementClass the type of elements contained in the publisher
|
||||
|
@ -132,17 +161,15 @@ public final class MultipartBodyBuilder {
|
|||
Assert.notNull(publisher, "'publisher' must not be null");
|
||||
Assert.notNull(elementClass, "'elementClass' must not be null");
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
PublisherPartBuilder<T, P> builder = new PublisherPartBuilder<>(headers, publisher, elementClass);
|
||||
PublisherPartBuilder<T, P> builder = new PublisherPartBuilder<>(null, publisher, elementClass);
|
||||
this.parts.add(name, builder);
|
||||
return builder;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #asyncPart(String, Publisher, Class)} that accepts a
|
||||
* {@link ParameterizedTypeReference} for the element type, which allows
|
||||
* specifying generic type information.
|
||||
* Variant of {@link #asyncPart(String, Publisher, Class)} with a
|
||||
* {@link ParameterizedTypeReference} for the element type information.
|
||||
* @param name the name of the part to add
|
||||
* @param publisher the part contents
|
||||
* @param typeReference the type of elements contained in the publisher
|
||||
|
@ -155,8 +182,7 @@ public final class MultipartBodyBuilder {
|
|||
Assert.notNull(publisher, "'publisher' must not be null");
|
||||
Assert.notNull(typeReference, "'typeReference' must not be null");
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
PublisherPartBuilder<T, P> builder = new PublisherPartBuilder<>(headers, publisher, typeReference);
|
||||
PublisherPartBuilder<T, P> builder = new PublisherPartBuilder<>(null, publisher, typeReference);
|
||||
this.parts.add(name, builder);
|
||||
return builder;
|
||||
}
|
||||
|
@ -201,28 +227,36 @@ public final class MultipartBodyBuilder {
|
|||
|
||||
private static class DefaultPartBuilder implements PartBuilder {
|
||||
|
||||
protected final HttpHeaders headers;
|
||||
@Nullable
|
||||
protected HttpHeaders headers;
|
||||
|
||||
@Nullable
|
||||
protected final Object body;
|
||||
|
||||
public DefaultPartBuilder(HttpHeaders headers, @Nullable Object body) {
|
||||
public DefaultPartBuilder(@Nullable HttpHeaders headers, @Nullable Object body) {
|
||||
this.headers = headers;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartBuilder header(String headerName, String... headerValues) {
|
||||
this.headers.addAll(headerName, Arrays.asList(headerValues));
|
||||
initHeadersIfNecessary().addAll(headerName, Arrays.asList(headerValues));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartBuilder headers(Consumer<HttpHeaders> headersConsumer) {
|
||||
headersConsumer.accept(this.headers);
|
||||
headersConsumer.accept(initHeadersIfNecessary());
|
||||
return this;
|
||||
}
|
||||
|
||||
private HttpHeaders initHeadersIfNecessary() {
|
||||
if (this.headers == null) {
|
||||
this.headers = new HttpHeaders();
|
||||
}
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
public HttpEntity<?> build() {
|
||||
return new HttpEntity<>(this.body, this.headers);
|
||||
}
|
||||
|
@ -233,14 +267,14 @@ public final class MultipartBodyBuilder {
|
|||
|
||||
private final ResolvableType resolvableType;
|
||||
|
||||
public PublisherPartBuilder(HttpHeaders headers, P body, Class<S> elementClass) {
|
||||
public PublisherPartBuilder(@Nullable HttpHeaders headers, P body, Class<S> elementClass) {
|
||||
super(headers, body);
|
||||
this.resolvableType = ResolvableType.forClass(elementClass);
|
||||
}
|
||||
|
||||
public PublisherPartBuilder(HttpHeaders headers, P body, ParameterizedTypeReference<S> typeReference) {
|
||||
public PublisherPartBuilder(@Nullable HttpHeaders headers, P body, ParameterizedTypeReference<S> typeRef) {
|
||||
super(headers, body);
|
||||
this.resolvableType = ResolvableType.forType(typeReference);
|
||||
this.resolvableType = ResolvableType.forType(typeRef);
|
||||
}
|
||||
|
||||
public PublisherPartBuilder(PublisherEntity<S, P> other) {
|
||||
|
@ -265,7 +299,7 @@ public final class MultipartBodyBuilder {
|
|||
* @param <T> the type contained in the publisher
|
||||
* @param <P> the publisher
|
||||
*/
|
||||
public static final class PublisherEntity<T, P extends Publisher<T>> extends HttpEntity<P>
|
||||
static final class PublisherEntity<T, P extends Publisher<T>> extends HttpEntity<P>
|
||||
implements ResolvableTypeProvider {
|
||||
|
||||
private final ResolvableType resolvableType;
|
||||
|
|
Loading…
Reference in New Issue