Polish @RequestPart support

This commit is contained in:
Rossen Stoyanchev 2018-03-21 18:26:13 -04:00
parent c1405ef140
commit 94c525cdc8
3 changed files with 46 additions and 22 deletions

View File

@ -312,6 +312,9 @@ public abstract class MimeTypeUtils {
} }
} }
/** /**
* Generate a random MIME boundary as bytes, often used in multipart mime types. * Generate a random MIME boundary as bytes, often used in multipart mime types.
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
package org.springframework.web.reactive.result.method.annotation; package org.springframework.web.reactive.result.method.annotation;
import java.util.Collections;
import java.util.List; import java.util.List;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
@ -75,25 +74,31 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageReaderArgu
boolean isRequired = (requestPart == null || requestPart.required()); boolean isRequired = (requestPart == null || requestPart.required());
String name = getPartName(parameter, requestPart); String name = getPartName(parameter, requestPart);
Flux<Part> partFlux = getPartValues(name, exchange); Flux<Part> values = exchange.getMultipartData()
if (isRequired) { .flatMapMany(map -> {
partFlux = partFlux.switchIfEmpty(Flux.error(getMissingPartException(name, parameter))); List<Part> parts = map.get(name);
} if (CollectionUtils.isEmpty(parts)) {
return isRequired ?
Flux.error(getMissingPartException(name, parameter)) :
Flux.empty();
}
return Flux.fromIterable(parts);
});
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(parameter.getParameterType()); ReactiveAdapter adapter = getAdapterRegistry().getAdapter(parameter.getParameterType());
MethodParameter elementType = adapter != null ? parameter.nested() : parameter; MethodParameter elementType = adapter != null ? parameter.nested() : parameter;
if (Part.class.isAssignableFrom(elementType.getNestedParameterType())) { if (Part.class.isAssignableFrom(elementType.getNestedParameterType())) {
if (adapter != null) { if (adapter != null) {
partFlux = adapter.isMultiValue() ? partFlux : partFlux.take(1); values = adapter.isMultiValue() ? values : values.take(1);
return Mono.just(adapter.fromPublisher(partFlux)); return Mono.just(adapter.fromPublisher(values));
} }
else { else {
return partFlux.next().cast(Object.class); return values.next().cast(Object.class);
} }
} }
return partFlux.next().flatMap(part -> { return values.next().flatMap(part -> {
ServerHttpRequest partRequest = new PartServerHttpRequest(exchange.getRequest(), part); ServerHttpRequest partRequest = new PartServerHttpRequest(exchange.getRequest(), part);
ServerWebExchange partExchange = exchange.mutate().request(partRequest).build(); ServerWebExchange partExchange = exchange.mutate().request(partRequest).build();
return readBody(parameter, isRequired, bindingContext, partExchange); return readBody(parameter, isRequired, bindingContext, partExchange);
@ -113,12 +118,6 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageReaderArgu
return partName; return partName;
} }
private Flux<Part> getPartValues(String name, ServerWebExchange exchange) {
return exchange.getMultipartData()
.filter(map -> !CollectionUtils.isEmpty(map.get(name)))
.flatMapIterable(map -> map.getOrDefault(name, Collections.emptyList()));
}
private ServerWebInputException getMissingPartException(String name, MethodParameter param) { private ServerWebInputException getMissingPartException(String name, MethodParameter param) {
String reason = "Required request part '" + name + "' is not present"; String reason = "Required request part '" + name + "' is not present";
return new ServerWebInputException(reason, param); return new ServerWebInputException(reason, param);

View File

@ -1789,27 +1789,26 @@ Content-Transfer-Encoding: 8bit
... File Data ... ... File Data ...
---- ----
You can access the "meta-data" part with `@RequestPart` which would deserialize it from You can access individual parts with `@RequestPart`:
JSON (similar to `@RequestBody`) through one of the configured <<webflux-codecs>>:
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@PostMapping("/") @PostMapping("/")
public String handle(**@RequestPart("meta-data") MetaData metadata, public String handle(**@RequestPart("meta-data") Part metadata,
@RequestPart("file-data") FilePart file**) { @RequestPart("file-data") FilePart file**) {
// ... // ...
} }
---- ----
To access multipart data sequentially, in streaming fashion, use `@RequestBody` with To deserialize the raw part content, for example to JSON (similar to `@RequestBody`),
`Flux<Part>` instead. For example: simply declare a concrete target Object, instead of `Part`:
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@PostMapping("/") @PostMapping("/")
public String handle(**@RequestBody Flux<Part> parts**) { public String handle(**@RequestPart("meta-data") MetaData metadata**) {
// ... // ...
} }
---- ----
@ -1830,6 +1829,29 @@ public String handle(**@Valid** @RequestPart("meta-data") MetaData metadata,
} }
---- ----
To access all multipart data in as a `MultiValueMap` use `@RequestBody`:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@PostMapping("/")
public String handle(**@RequestBody Mono<MultiValueMap<String, Part>> parts**) {
// ...
}
----
To access multipart data sequentially, in streaming fashion, use `@RequestBody` with
`Flux<Part>` instead. For example:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@PostMapping("/")
public String handle(**@RequestBody Flux<Part> parts**) {
// ...
}
----
[[webflux-ann-requestbody]] [[webflux-ann-requestbody]]
==== @RequestBody ==== @RequestBody