Consistent support for path variable and multipart binding
Closes gh-24107 Closes gh-22169 Closes gh-25265
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,16 @@
|
||||
|
||||
package org.springframework.web.reactive;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.validation.support.BindingAwareConcurrentModel;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebExchangeDataBinder;
|
||||
@@ -35,6 +43,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* <p>Container for the default model for the request.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.0
|
||||
*/
|
||||
public class BindingContext {
|
||||
@@ -79,7 +88,7 @@ public class BindingContext {
|
||||
* @throws ServerErrorException if {@code @InitBinder} method invocation fails
|
||||
*/
|
||||
public WebExchangeDataBinder createDataBinder(ServerWebExchange exchange, @Nullable Object target, String name) {
|
||||
WebExchangeDataBinder dataBinder = new WebExchangeDataBinder(target, name);
|
||||
WebExchangeDataBinder dataBinder = new ExtendedWebExchangeDataBinder(target, name);
|
||||
if (this.initializer != null) {
|
||||
this.initializer.initBinder(dataBinder);
|
||||
}
|
||||
@@ -106,4 +115,34 @@ public class BindingContext {
|
||||
return createDataBinder(exchange, null, name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extended variant of {@link WebExchangeDataBinder}, adding path variables.
|
||||
*/
|
||||
private static class ExtendedWebExchangeDataBinder extends WebExchangeDataBinder {
|
||||
|
||||
public ExtendedWebExchangeDataBinder(@Nullable Object target, String objectName) {
|
||||
super(target, objectName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Map<String, Object>> getValuesToBind(ServerWebExchange exchange) {
|
||||
Map<String, String> vars = exchange.getAttributeOrDefault(
|
||||
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, Collections.emptyMap());
|
||||
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
|
||||
Mono<MultiValueMap<String, String>> formData = exchange.getFormData();
|
||||
Mono<MultiValueMap<String, Part>> multipartData = exchange.getMultipartData();
|
||||
|
||||
return Mono.zip(Mono.just(vars), Mono.just(queryParams), formData, multipartData)
|
||||
.map(tuple -> {
|
||||
Map<String, Object> result = new TreeMap<>();
|
||||
tuple.getT1().forEach(result::put);
|
||||
tuple.getT2().forEach((key, values) -> addBindValue(result, key, values));
|
||||
tuple.getT3().forEach((key, values) -> addBindValue(result, key, values));
|
||||
tuple.getT4().forEach((key, values) -> addBindValue(result, key, values));
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.springframework.util.ClassUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.support.WebExchangeBindException;
|
||||
import org.springframework.web.bind.support.WebExchangeDataBinder;
|
||||
@@ -233,7 +232,8 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
}
|
||||
|
||||
// A single data class constructor -> resolve constructor arguments from request parameters.
|
||||
return WebExchangeDataBinder.extractValuesToBind(exchange).map(bindValues -> {
|
||||
WebExchangeDataBinder binder = context.createDataBinder(exchange, null, attributeName);
|
||||
return getValuesToBind(binder, exchange).map(bindValues -> {
|
||||
ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class);
|
||||
String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor));
|
||||
Assert.state(paramNames != null, () -> "Cannot resolve parameter names for constructor " + ctor);
|
||||
@@ -241,7 +241,6 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
Assert.state(paramNames.length == paramTypes.length,
|
||||
() -> "Invalid number of parameter names: " + paramNames.length + " for constructor " + ctor);
|
||||
Object[] args = new Object[paramTypes.length];
|
||||
WebDataBinder binder = context.createDataBinder(exchange, null, attributeName);
|
||||
String fieldDefaultPrefix = binder.getFieldDefaultPrefix();
|
||||
String fieldMarkerPrefix = binder.getFieldMarkerPrefix();
|
||||
for (int i = 0; i < paramNames.length; i++) {
|
||||
@@ -271,6 +270,18 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected method to obtain the values for data binding. By default this
|
||||
* method delegates to {@link WebExchangeDataBinder#getValuesToBind}.
|
||||
* @param binder the data binder in use
|
||||
* @param exchange the current exchange
|
||||
* @return a map of bind values
|
||||
* @since 5.3
|
||||
*/
|
||||
public Mono<Map<String, Object>> getValuesToBind(WebExchangeDataBinder binder, ServerWebExchange exchange) {
|
||||
return binder.getValuesToBind(exchange);
|
||||
}
|
||||
|
||||
private boolean hasErrorsArgument(MethodParameter parameter) {
|
||||
int i = parameter.getParameterIndex();
|
||||
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
|
||||
|
||||
Reference in New Issue
Block a user