Polish MultipartBodyBuilder

Improve Javadoc
Consistently reject Publisher unless using asyncPart
Consistently set Content-Type when specified
master
Rossen Stoyanchev 5 years ago
parent 6eac141160
commit 49e5c4dcf6
  1. 108
      spring-web/src/main/java/org/springframework/http/client/MultipartBodyBuilder.java

@ -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&lt;Account&gt; accountMono = ...
* builder.asyncPart("account", accountMono).header("foo", "bar");
*
* // Build and use
* MultiValueMap&lt;String, HttpEntity&lt;?&gt;&gt; multipartBody = builder.build();
* // use multipartBody with RestTemplate or WebClient
*
* Mono&lt;Void&gt; 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…
Cancel
Save