提交 6f3051c6 编写于 作者: R Rossen Stoyanchev

Support for @RequestPart with reactive type wrapper

Issue: SPR-14546
上级 fc7beded
...@@ -48,9 +48,6 @@ import org.springframework.web.server.ServerWebExchange; ...@@ -48,9 +48,6 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession; import org.springframework.web.server.WebSession;
import org.springframework.web.server.session.WebSessionManager; import org.springframework.web.server.session.WebSessionManager;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
/** /**
* Default implementation of {@link ServerWebExchange}. * Default implementation of {@link ServerWebExchange}.
* *
...@@ -61,10 +58,10 @@ public class DefaultServerWebExchange implements ServerWebExchange { ...@@ -61,10 +58,10 @@ public class DefaultServerWebExchange implements ServerWebExchange {
private static final List<HttpMethod> SAFE_METHODS = Arrays.asList(HttpMethod.GET, HttpMethod.HEAD); private static final List<HttpMethod> SAFE_METHODS = Arrays.asList(HttpMethod.GET, HttpMethod.HEAD);
private static final ResolvableType FORM_DATA_VALUE_TYPE = private static final ResolvableType FORM_DATA_TYPE =
ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class); ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class);
private static final ResolvableType MULTIPART_VALUE_TYPE = ResolvableType.forClassWithGenerics( private static final ResolvableType MULTIPART_DATA_TYPE = ResolvableType.forClassWithGenerics(
MultiValueMap.class, String.class, Part.class); MultiValueMap.class, String.class, Part.class);
private static final Mono<MultiValueMap<String, String>> EMPTY_FORM_DATA = private static final Mono<MultiValueMap<String, String>> EMPTY_FORM_DATA =
...@@ -110,21 +107,17 @@ public class DefaultServerWebExchange implements ServerWebExchange { ...@@ -110,21 +107,17 @@ public class DefaultServerWebExchange implements ServerWebExchange {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static Mono<MultiValueMap<String, String>> initFormData( private static Mono<MultiValueMap<String, String>> initFormData(ServerHttpRequest request,
ServerHttpRequest request, ServerCodecConfigurer codecConfigurer) { ServerCodecConfigurer configurer) {
MediaType contentType;
try { try {
contentType = request.getHeaders().getContentType(); MediaType contentType = request.getHeaders().getContentType();
if (APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) { if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
return ((HttpMessageReader<MultiValueMap<String, String>>)codecConfigurer return ((HttpMessageReader<MultiValueMap<String, String>>) configurer.getReaders().stream()
.getReaders() .filter(reader -> reader.canRead(FORM_DATA_TYPE, MediaType.APPLICATION_FORM_URLENCODED))
.stream()
.filter(reader -> reader.canRead(FORM_DATA_VALUE_TYPE, APPLICATION_FORM_URLENCODED))
.findFirst() .findFirst()
.orElseThrow(() -> new IllegalStateException( .orElseThrow(() -> new IllegalStateException("No form data HttpMessageReader.")))
"Could not find HttpMessageReader that supports " + APPLICATION_FORM_URLENCODED))) .readMono(FORM_DATA_TYPE, request, Collections.emptyMap())
.readMono(FORM_DATA_VALUE_TYPE, request, Collections.emptyMap())
.switchIfEmpty(EMPTY_FORM_DATA) .switchIfEmpty(EMPTY_FORM_DATA)
.cache(); .cache();
} }
...@@ -136,21 +129,17 @@ public class DefaultServerWebExchange implements ServerWebExchange { ...@@ -136,21 +129,17 @@ public class DefaultServerWebExchange implements ServerWebExchange {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static Mono<MultiValueMap<String, Part>> initMultipartData( private static Mono<MultiValueMap<String, Part>> initMultipartData(ServerHttpRequest request,
ServerHttpRequest request, ServerCodecConfigurer codecConfigurer) { ServerCodecConfigurer configurer) {
MediaType contentType;
try { try {
contentType = request.getHeaders().getContentType(); MediaType contentType = request.getHeaders().getContentType();
if (MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
return ((HttpMessageReader<MultiValueMap<String, Part>>) codecConfigurer return ((HttpMessageReader<MultiValueMap<String, Part>>) configurer.getReaders().stream()
.getReaders() .filter(reader -> reader.canRead(MULTIPART_DATA_TYPE, MediaType.MULTIPART_FORM_DATA))
.stream()
.filter(reader -> reader.canRead(MULTIPART_VALUE_TYPE, MULTIPART_FORM_DATA))
.findFirst() .findFirst()
.orElseThrow(() -> new IllegalStateException( .orElseThrow(() -> new IllegalStateException("No multipart HttpMessageReader.")))
"Could not find HttpMessageReader that supports " + MULTIPART_FORM_DATA))) .readMono(MULTIPART_DATA_TYPE, request, Collections.emptyMap())
.readMono(FORM_DATA_VALUE_TYPE, request, Collections.emptyMap())
.switchIfEmpty(EMPTY_MULTIPART_DATA) .switchIfEmpty(EMPTY_MULTIPART_DATA)
.cache(); .cache();
} }
......
...@@ -21,6 +21,7 @@ import java.util.List; ...@@ -21,6 +21,7 @@ import java.util.List;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ReactiveAdapter;
import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.http.codec.multipart.Part; import org.springframework.http.codec.multipart.Part;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
...@@ -61,13 +62,16 @@ public class RequestPartMethodArgumentResolver extends AbstractNamedValueArgumen ...@@ -61,13 +62,16 @@ public class RequestPartMethodArgumentResolver extends AbstractNamedValueArgumen
@Override @Override
protected Mono<Object> resolveName(String name, MethodParameter param, ServerWebExchange exchange) { protected Mono<Object> resolveName(String name, MethodParameter param, ServerWebExchange exchange) {
return exchange.getMultipartData().flatMap(allParts -> {
List<Part> parts = allParts.get(name); Mono<Object> partsMono = exchange.getMultipartData()
if (CollectionUtils.isEmpty(parts)) { .filter(map -> !CollectionUtils.isEmpty(map.get(name)))
return Mono.empty(); .map(map -> {
} List<Part> parts = map.get(name);
return Mono.just(parts.size() == 1 ? parts.get(0) : parts); return parts.size() == 1 ? parts.get(0) : parts;
}); });
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(param.getParameterType());
return (adapter != null ? Mono.just(adapter.fromPublisher(partsMono)) : partsMono);
} }
@Override @Override
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.web.reactive.result.method.annotation; package org.springframework.web.reactive.result.method.annotation;
import java.time.Duration;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -34,6 +35,7 @@ import org.springframework.http.HttpHeaders; ...@@ -34,6 +35,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart; import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.codec.multipart.Part; import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests; import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.HttpHandler;
...@@ -152,8 +154,9 @@ public class MultipartIntegrationTests extends AbstractHttpHandlerIntegrationTes ...@@ -152,8 +154,9 @@ public class MultipartIntegrationTests extends AbstractHttpHandlerIntegrationTes
static class MultipartController { static class MultipartController {
@PostMapping("/requestPart") @PostMapping("/requestPart")
void requestPart(@RequestPart Part fooPart) { void requestPart(@RequestPart FormFieldPart barPart, @RequestPart Mono<FilePart> fooPart) {
assertEquals("foo.txt", ((FilePart) fooPart).getFilename()); assertEquals("bar", barPart.getValue());
assertEquals("foo.txt", fooPart.block(Duration.ZERO).getFilename());
} }
@PostMapping("/requestBodyMap") @PostMapping("/requestBodyMap")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册