Fix multi-part form handling for WebFlux apps

In WebFlux (but not MVC) you have to explicitly ask for the multi-
part content separately from the form data.

Fixes gh-223
This commit is contained in:
Dave Syer
2018-10-26 09:53:17 +01:00
parent a735f50daa
commit bf9ab3ac0a
4 changed files with 43 additions and 5 deletions

View File

@@ -27,7 +27,11 @@ import org.springframework.cloud.function.web.RequestProcessor.FunctionWrapper;
import org.springframework.cloud.function.web.constants.WebRequestConstants;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -49,13 +53,34 @@ public class FunctionController {
this.processor = processor;
}
@PostMapping(path = "/**", consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE })
@PostMapping(path = "/**", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public Mono<ResponseEntity<?>> form(ServerWebExchange request) {
FunctionWrapper wrapper = wrapper(request);
return request.getFormData().doOnSuccess(params -> wrapper.params(params))
.then(processor.post(wrapper, null, false));
.then(Mono.defer(() -> processor.post(wrapper, null, false)));
}
@PostMapping(path = "/**", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public Mono<ResponseEntity<?>> multipart(ServerWebExchange request) {
FunctionWrapper wrapper = wrapper(request);
return request.getMultipartData()
.doOnSuccess(params -> wrapper.params(multi(params)))
.then(Mono.defer(() -> processor.post(wrapper, null, false)));
}
private MultiValueMap<String, String> multi(MultiValueMap<String, Part> body) {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
for (String key : body.keySet()) {
for (Part part : body.get(key)) {
if (part instanceof FormFieldPart) {
FormFieldPart form = (FormFieldPart) part;
map.add(key, form.value());
}
}
}
return map;
}
@PostMapping(path = "/**")

View File

@@ -302,7 +302,6 @@ public class HttpPostIntegrationTests {
}
@Test
@Ignore
public void multipart() throws Exception {
LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();

View File

@@ -307,6 +307,20 @@ public class HttpPostIntegrationTests {
String.class).getBody()).isEqualTo("[{\"A\":6,\"B\":11}]");
}
@Test
public void multipart() throws Exception {
LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put("A", Arrays.asList("1", "2", "3"));
map.put("B", Arrays.asList("5", "6"));
assertThat(rest.exchange(
RequestEntity.post(new URI("/sum")).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.MULTIPART_FORM_DATA).body(map),
String.class).getBody()).isEqualTo("[{\"A\":6,\"B\":11}]");
}
@Test
public void count() throws Exception {
List<String> list = Arrays.asList("A", "B", "A");