Add ResolvableType to HttpEntity for multipart Publishers

This commit adds a ResolvableType field to HttpEntity, in order to
support Publishers as multipart data. Without the type, the
MultipartHttpMessageWriter does not know which delegate writer to use to
write the part.

Issue: SPR-16307
This commit is contained in:
Arjen Poutsma
2017-12-20 15:09:37 +01:00
parent 9d27e86951
commit f23612c3a3
5 changed files with 216 additions and 20 deletions

View File

@@ -17,7 +17,10 @@
package org.springframework.http.client;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
@@ -34,23 +37,25 @@ public class MultipartBodyBuilderTests {
@Test
public void builder() throws Exception {
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.add("form field", "form value");
MultiValueMap<String, String> multipartData = new LinkedMultiValueMap<>();
multipartData.add("form field", "form value");
Resource logo = new ClassPathResource("/org/springframework/http/converter/logo.jpg");
HttpHeaders entityHeaders = new HttpHeaders();
entityHeaders.add("foo", "bar");
HttpEntity<String> entity = new HttpEntity<>("body", entityHeaders);
Publisher<String> publisher = Flux.just("foo", "bar", "baz");
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("key", form).header("foo", "bar");
builder.part("key", multipartData).header("foo", "bar");
builder.part("logo", logo).header("baz", "qux");
builder.part("entity", entity).header("baz", "qux");
builder.asyncPart("publisher", publisher, String.class).header("baz", "qux");
MultiValueMap<String, HttpEntity<?>> result = builder.build();
assertEquals(3, result.size());
assertEquals(4, result.size());
assertNotNull(result.getFirst("key"));
assertEquals(form, result.getFirst("key").getBody());
assertEquals(multipartData, result.getFirst("key").getBody());
assertEquals("bar", result.getFirst("key").getHeaders().getFirst("foo"));
assertNotNull(result.getFirst("logo"));
@@ -61,6 +66,12 @@ public class MultipartBodyBuilderTests {
assertEquals("body", result.getFirst("entity").getBody());
assertEquals("bar", result.getFirst("entity").getHeaders().getFirst("foo"));
assertEquals("qux", result.getFirst("entity").getHeaders().getFirst("baz"));
assertNotNull(result.getFirst("publisher"));
assertEquals(publisher, result.getFirst("publisher").getBody());
assertEquals(ResolvableType.forClass(String.class), result.getFirst("publisher").getBodyType());
assertEquals("bar", result.getFirst("entity").getHeaders().getFirst("foo"));
assertEquals("qux", result.getFirst("entity").getHeaders().getFirst("baz"));
}

View File

@@ -22,6 +22,8 @@ import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ResolvableType;
@@ -36,10 +38,7 @@ import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
import org.springframework.util.MultiValueMap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
/**
* @author Sebastien Deleuze
@@ -80,6 +79,8 @@ public class MultipartHttpMessageWriterTests {
}
};
Publisher<String> publisher = Flux.just("foo", "bar", "baz");
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
bodyBuilder.part("name 1", "value 1");
bodyBuilder.part("name 2", "value 2+1");
@@ -87,14 +88,15 @@ public class MultipartHttpMessageWriterTests {
bodyBuilder.part("logo", logo);
bodyBuilder.part("utf8", utf8);
bodyBuilder.part("json", new Foo("bar"), MediaType.APPLICATION_JSON_UTF8);
Mono<MultiValueMap<String, HttpEntity<?>>> publisher = Mono.just(bodyBuilder.build());
bodyBuilder.asyncPart("publisher", publisher, String.class);
Mono<MultiValueMap<String, HttpEntity<?>>> result = Mono.just(bodyBuilder.build());
MockServerHttpResponse response = new MockServerHttpResponse();
Map<String, Object> hints = Collections.emptyMap();
this.writer.write(publisher, null, MediaType.MULTIPART_FORM_DATA, response, hints).block(Duration.ofSeconds(5));
this.writer.write(result, null, MediaType.MULTIPART_FORM_DATA, response, hints).block(Duration.ofSeconds(5));
MultiValueMap<String, Part> requestParts = parse(response, hints);
assertEquals(5, requestParts.size());
assertEquals(6, requestParts.size());
Part part = requestParts.getFirst("name 1");
assertTrue(part instanceof FormFieldPart);
@@ -136,6 +138,17 @@ public class MultipartHttpMessageWriterTests {
assertEquals("{\"bar\":\"bar\"}", value);
part = requestParts.getFirst("publisher");
assertEquals("publisher", part.name());
value = StringDecoder.textPlainOnly(false).decodeToMono(part.content(),
ResolvableType.forClass(String.class), MediaType.TEXT_PLAIN,
Collections.emptyMap()).block(Duration.ZERO);
assertEquals("foobarbaz", value);
}
private MultiValueMap<String, Part> parse(MockServerHttpResponse response, Map<String, Object> hints) {