All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.http.client.MultipartBodyBuilder Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2018 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.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.http.client;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import org.reactivestreams.Publisher;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
 * A mutable builder for multipart form bodies. For example:
 * 
 *
 * MultipartBodyBuilder builder = new MultipartBodyBuilder();
 * builder.part("form field", "form value");
 *
 * Resource image = new ClassPathResource("image.jpg");
 * builder.part("image", image).header("Baz", "Qux");
 *
 * MultiValueMap> multipartBody = builder.build();
 * // use multipartBody with RestTemplate or WebClient
 * 
* @author Arjen Poutsma * @since 5.0.2 * @see RFC 7578 */ public final class MultipartBodyBuilder { private final LinkedMultiValueMap parts = new LinkedMultiValueMap<>(); /** * Creates a new, empty instance of the {@code MultipartBodyBuilder}. */ public MultipartBodyBuilder() { } /** * Builds the multipart body. * @return the built body */ public MultiValueMap> build() { MultiValueMap> result = new LinkedMultiValueMap<>(this.parts.size()); for (Map.Entry> entry : this.parts.entrySet()) { for (DefaultPartBuilder builder : entry.getValue()) { HttpEntity entity = builder.build(); result.add(entry.getKey(), entity); } } return result; } /** * Adds a part to this builder, allowing for further header customization with the returned * {@link PartBuilder}. * @param name the name of the part to add (may not be empty) * @param part the part to add * @return a builder that allows for further header customization */ public PartBuilder part(String name, Object part) { return part(name, part, null); } /** * Adds a part to this builder, allowing for further header customization with the returned * {@link PartBuilder}. * @param name the name of the part to add (may not be empty) * @param part the part to add * @param contentType the {@code Content-Type} header for the part (may be {@code null}) * @return a builder that allows for further header customization */ 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"); } Object partBody; HttpHeaders partHeaders = new HttpHeaders(); if (part instanceof HttpEntity) { HttpEntity other = (HttpEntity) part; partBody = other.getBody(); partHeaders.addAll(other.getHeaders()); } else { partBody = part; } if (contentType != null) { partHeaders.setContentType(contentType); } DefaultPartBuilder builder = new DefaultPartBuilder(partBody, partHeaders); this.parts.add(name, builder); return builder; } /** * Adds a {@link Publisher} part to this builder, allowing for further header customization with * the returned {@link PartBuilder}. * @param name the name of the part to add (may not be empty) * @param publisher the contents of the part to add * @param elementClass the class of elements contained in the publisher * @return a builder that allows for further header customization */ public > PartBuilder asyncPart(String name, P publisher, Class elementClass) { Assert.notNull(elementClass, "'elementClass' must not be null"); ResolvableType elementType = ResolvableType.forClass(elementClass); Assert.hasLength(name, "'name' must not be empty"); Assert.notNull(publisher, "'publisher' must not be null"); Assert.notNull(elementType, "'elementType' must not be null"); HttpHeaders partHeaders = new HttpHeaders(); PublisherPartBuilder builder = new PublisherPartBuilder<>(publisher, elementClass, partHeaders); this.parts.add(name, builder); return builder; } /** * Adds a {@link Publisher} part to this builder, allowing for further header customization with * the returned {@link PartBuilder}. * @param name the name of the part to add (may not be empty) * @param publisher the contents of the part to add * @param typeReference the type of elements contained in the publisher * @return a builder that allows for further header customization */ public > PartBuilder asyncPart(String name, P publisher, ParameterizedTypeReference typeReference) { Assert.notNull(typeReference, "'typeReference' must not be null"); ResolvableType elementType1 = ResolvableType.forType(typeReference); Assert.hasLength(name, "'name' must not be empty"); Assert.notNull(publisher, "'publisher' must not be null"); Assert.notNull(elementType1, "'typeReference' must not be null"); HttpHeaders partHeaders = new HttpHeaders(); PublisherPartBuilder builder = new PublisherPartBuilder<>(publisher, typeReference, partHeaders); this.parts.add(name, builder); return builder; } /** * Builder interface that allows for customization of part headers. */ public interface PartBuilder { /** * Add the given part-specific header values under the given name. * @param headerName the part header name * @param headerValues the part header value(s) * @return this builder * @see HttpHeaders#add(String, String) */ PartBuilder header(String headerName, String... headerValues); /** * Manipulate the part's headers with the given consumer. * @param headersConsumer a function that consumes the {@code HttpHeaders} * @return this builder */ PartBuilder headers(Consumer headersConsumer); } private static class DefaultPartBuilder implements PartBuilder { @Nullable protected final Object body; protected final HttpHeaders headers; public DefaultPartBuilder(@Nullable Object body, HttpHeaders headers) { this.body = body; this.headers = headers; } @Override public PartBuilder header(String headerName, String... headerValues) { this.headers.addAll(headerName, Arrays.asList(headerValues)); return this; } @Override public PartBuilder headers(Consumer headersConsumer) { Assert.notNull(headersConsumer, "'headersConsumer' must not be null"); headersConsumer.accept(this.headers); return this; } public HttpEntity build() { return new HttpEntity<>(this.body, this.headers); } } private static class PublisherPartBuilder> extends DefaultPartBuilder { private final ResolvableType resolvableType; public PublisherPartBuilder(P body, Class elementClass, HttpHeaders headers) { super(body, headers); this.resolvableType = ResolvableType.forClass(elementClass); } public PublisherPartBuilder(P body, ParameterizedTypeReference typeReference, HttpHeaders headers) { super(body, headers); this.resolvableType = ResolvableType.forType(typeReference); } @Override @SuppressWarnings("unchecked") public HttpEntity build() { P publisher = (P) this.body; Assert.state(publisher != null, "'publisher' must not be null"); return new PublisherEntity<>(publisher, this.resolvableType, this.headers); } } /** * Specific subtype of {@link HttpEntity} for containing {@link Publisher}s as body. * Exposes the type contained in the publisher through {@link #getResolvableType()}. * @param The type contained in the publisher * @param

The publisher */ public static final class PublisherEntity> extends HttpEntity

{ private final ResolvableType resolvableType; PublisherEntity(P publisher, ResolvableType resolvableType, @Nullable MultiValueMap headers) { super(publisher, headers); Assert.notNull(publisher, "'publisher' must not be null"); Assert.notNull(resolvableType, "'resolvableType' must not be null"); this.resolvableType = resolvableType; } /** * Return the resolvable type for this entry. */ public ResolvableType getResolvableType() { return this.resolvableType; } } }