Add support for Flux<Part> in BodyExtractors

This commit adds a `toParts` method in `BodyExtractors`, returning a
BodyExtractor<Part>.
This commit is contained in:
Arjen Poutsma
2017-05-04 12:21:48 +02:00
parent 1f5eaf20b0
commit 4525c6a537
3 changed files with 144 additions and 30 deletions

View File

@@ -36,6 +36,8 @@ import reactor.test.StepVerifier;
import org.springframework.core.codec.ByteBufferDecoder;
import org.springframework.core.codec.StringDecoder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
@@ -45,10 +47,16 @@ import org.springframework.http.codec.DecoderHttpMessageReader;
import org.springframework.http.codec.FormHttpMessageReader;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader;
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.MultiValueMap;
import static org.junit.Assert.*;
@@ -72,6 +80,11 @@ public class BodyExtractorsTests {
messageReaders.add(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true)));
messageReaders.add(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
messageReaders.add(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()));
messageReaders.add(new FormHttpMessageReader());
SynchronossPartHttpMessageReader partReader = new SynchronossPartHttpMessageReader();
messageReaders.add(partReader);
messageReaders.add(new MultipartHttpMessageReader(partReader));
messageReaders.add(new FormHttpMessageReader());
this.context = new BodyExtractor.Context() {
@@ -249,6 +262,64 @@ public class BodyExtractorsTests {
.verify();
}
@Test
public void toParts() throws Exception {
BodyExtractor<Flux<Part>, ServerHttpRequest> extractor = BodyExtractors.toParts();
String bodyContents = "-----------------------------9051914041544843365972754266\r\n" +
"Content-Disposition: form-data; name=\"text\"\r\n" +
"\r\n" +
"text default\r\n" +
"-----------------------------9051914041544843365972754266\r\n" +
"Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\"\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n" +
"Content of a.txt.\r\n" +
"\r\n" +
"-----------------------------9051914041544843365972754266\r\n" +
"Content-Disposition: form-data; name=\"file2\"; filename=\"a.html\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"<!DOCTYPE html><title>Content of a.html.</title>\r\n" +
"\r\n" +
"-----------------------------9051914041544843365972754266--\r\n";
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap(bodyContents.getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
MockServerHttpRequest request = MockServerHttpRequest.post("/")
.header("Content-Type", "multipart/form-data; boundary=---------------------------9051914041544843365972754266")
.body(body);
Flux<Part> result = extractor.extract(request, this.context);
StepVerifier.create(result)
.consumeNextWith(part -> {
assertEquals("text", part.getName());
assertTrue(part instanceof FormFieldPart);
FormFieldPart formFieldPart = (FormFieldPart) part;
assertEquals("text default", formFieldPart.getValue());
})
.consumeNextWith(part -> {
assertEquals("file1", part.getName());
assertTrue(part instanceof FilePart);
FilePart filePart = (FilePart) part;
assertEquals("a.txt", filePart.getFilename());
assertEquals(MediaType.TEXT_PLAIN, filePart.getHeaders().getContentType());
})
.consumeNextWith(part -> {
assertEquals("file2", part.getName());
assertTrue(part instanceof FilePart);
FilePart filePart = (FilePart) part;
assertEquals("a.html", filePart.getFilename());
assertEquals(MediaType.TEXT_HTML, filePart.getHeaders().getContentType());
})
.expectComplete()
.verify();
}
@Test
public void toDataBuffers() throws Exception {
BodyExtractor<Flux<DataBuffer>, ReactiveHttpInputMessage> extractor = BodyExtractors.toDataBuffers();

View File

@@ -16,6 +16,7 @@
package org.springframework.web.reactive.function;
import java.util.List;
import java.util.Map;
import org.junit.Test;
@@ -48,10 +49,25 @@ public class MultipartIntegrationTests extends AbstractRouterFunctionIntegration
private final WebClient webClient = WebClient.create();
@Test
public void multipart() {
public void multipartData() {
Mono<ClientResponse> result = webClient
.post()
.uri("http://localhost:" + this.port + "/")
.uri("http://localhost:" + this.port + "/multipartData")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(generateBody()))
.exchange();
StepVerifier
.create(result)
.consumeNextWith(response -> assertEquals(HttpStatus.OK, response.statusCode()))
.verifyComplete();
}
@Test
public void parts() {
Mono<ClientResponse> result = webClient
.post()
.uri("http://localhost:" + this.port + "/parts")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(generateBody()))
.exchange();
@@ -77,12 +93,13 @@ public class MultipartIntegrationTests extends AbstractRouterFunctionIntegration
@Override
protected RouterFunction<ServerResponse> routerFunction() {
MultipartHandler multipartHandler = new MultipartHandler();
return route(POST("/"), multipartHandler::handle);
return route(POST("/multipartData"), multipartHandler::multipartData)
.andRoute(POST("/parts"), multipartHandler::parts);
}
private static class MultipartHandler {
public Mono<ServerResponse> handle(ServerRequest request) {
public Mono<ServerResponse> multipartData(ServerRequest request) {
return request
.body(BodyExtractors.toMultipartData())
.flatMap(map -> {
@@ -98,6 +115,21 @@ public class MultipartIntegrationTests extends AbstractRouterFunctionIntegration
return ServerResponse.ok().build();
});
}
public Mono<ServerResponse> parts(ServerRequest request) {
return request.body(BodyExtractors.toParts()).collectList()
.flatMap(parts -> {
try {
assertEquals(2, parts.size());
assertEquals("foo.txt", ((FilePart) parts.get(0)).getFilename());
assertEquals("bar", ((FormFieldPart) parts.get(1)).getValue());
}
catch(Exception e) {
return Mono.error(e);
}
return ServerResponse.ok().build();
});
}
}
}