Revised common validation methods in AbstractMessageConverterMethodArgumentResolver
The protected validation methods are analogous to ModelAttributeMethodProcessor now. Issue: SPR-12655
This commit is contained in:
parent
65b6017db9
commit
7191050e26
|
|
@ -109,15 +109,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
if (binder.getTarget() != null) {
|
if (binder.getTarget() != null) {
|
||||||
bindRequestParameters(binder, webRequest);
|
bindRequestParameters(binder, webRequest);
|
||||||
validateIfApplicable(binder, parameter);
|
validateIfApplicable(binder, parameter);
|
||||||
if (binder.getBindingResult().hasErrors()) {
|
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
|
||||||
if (isBindExceptionRequired(binder, parameter)) {
|
throw new BindException(binder.getBindingResult());
|
||||||
throw new BindException(binder.getBindingResult());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add resolved attribute and BindingResult at the end of the model
|
// Add resolved attribute and BindingResult at the end of the model
|
||||||
|
|
||||||
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
|
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
|
||||||
mavContainer.removeAttributes(bindingResultModel);
|
mavContainer.removeAttributes(bindingResultModel);
|
||||||
mavContainer.addAllAttributes(bindingResultModel);
|
mavContainer.addAllAttributes(bindingResultModel);
|
||||||
|
|
@ -129,15 +126,15 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
* Extension point to create the model attribute if not found in the model.
|
* Extension point to create the model attribute if not found in the model.
|
||||||
* The default implementation uses the default constructor.
|
* The default implementation uses the default constructor.
|
||||||
* @param attributeName the name of the attribute (never {@code null})
|
* @param attributeName the name of the attribute (never {@code null})
|
||||||
* @param parameter the method parameter
|
* @param methodParam the method parameter
|
||||||
* @param binderFactory for creating WebDataBinder instance
|
* @param binderFactory for creating WebDataBinder instance
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @return the created model attribute (never {@code null})
|
* @return the created model attribute (never {@code null})
|
||||||
*/
|
*/
|
||||||
protected Object createAttribute(String attributeName, MethodParameter parameter,
|
protected Object createAttribute(String attributeName, MethodParameter methodParam,
|
||||||
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
|
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
|
||||||
|
|
||||||
return BeanUtils.instantiateClass(parameter.getParameterType());
|
return BeanUtils.instantiateClass(methodParam.getParameterType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -155,10 +152,10 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
* Spring's {@link org.springframework.validation.annotation.Validated},
|
* Spring's {@link org.springframework.validation.annotation.Validated},
|
||||||
* and custom annotations whose name starts with "Valid".
|
* and custom annotations whose name starts with "Valid".
|
||||||
* @param binder the DataBinder to be used
|
* @param binder the DataBinder to be used
|
||||||
* @param parameter the method parameter
|
* @param methodParam the method parameter
|
||||||
*/
|
*/
|
||||||
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
|
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
|
||||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
Annotation[] annotations = methodParam.getParameterAnnotations();
|
||||||
for (Annotation ann : annotations) {
|
for (Annotation ann : annotations) {
|
||||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
||||||
|
|
@ -171,16 +168,15 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to raise a {@link BindException} on validation errors.
|
* Whether to raise a fatal bind exception on validation errors.
|
||||||
* @param binder the data binder used to perform data binding
|
* @param binder the data binder used to perform data binding
|
||||||
* @param parameter the method argument
|
* @param methodParam the method argument
|
||||||
* @return {@code true} if the next method argument is not of type {@link Errors}.
|
* @return {@code true} if the next method argument is not of type {@link Errors}
|
||||||
*/
|
*/
|
||||||
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
|
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
|
||||||
int i = parameter.getParameterIndex();
|
int i = methodParam.getParameterIndex();
|
||||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
|
||||||
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||||
|
|
||||||
return !hasBindingResult;
|
return !hasBindingResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.web.servlet.mvc.method.annotation;
|
package org.springframework.web.servlet.mvc.method.annotation;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
@ -30,6 +31,7 @@ import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.http.HttpInputMessage;
|
import org.springframework.http.HttpInputMessage;
|
||||||
import org.springframework.http.InvalidMediaTypeException;
|
import org.springframework.http.InvalidMediaTypeException;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
|
@ -38,7 +40,9 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.http.server.ServletServerHttpRequest;
|
import org.springframework.http.server.ServletServerHttpRequest;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.validation.Errors;
|
import org.springframework.validation.Errors;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||||
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
|
|
||||||
|
|
@ -113,8 +117,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||||
* @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
|
* @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter methodParam,
|
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
|
||||||
Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
|
MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||||
|
|
||||||
MediaType contentType;
|
MediaType contentType;
|
||||||
try {
|
try {
|
||||||
|
|
@ -171,14 +175,38 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to raise a handler method invocation exception on validation errors.
|
* Validate the request part if applicable.
|
||||||
* @param parameter the method argument
|
* <p>The default implementation checks for {@code @javax.validation.Valid},
|
||||||
|
* Spring's {@link org.springframework.validation.annotation.Validated},
|
||||||
|
* and custom annotations whose name starts with "Valid".
|
||||||
|
* @param binder the DataBinder to be used
|
||||||
|
* @param methodParam the method parameter
|
||||||
|
* @see #isBindExceptionRequired
|
||||||
|
* @since 4.1.5
|
||||||
|
*/
|
||||||
|
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
|
||||||
|
Annotation[] annotations = methodParam.getParameterAnnotations();
|
||||||
|
for (Annotation ann : annotations) {
|
||||||
|
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||||
|
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
||||||
|
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
|
||||||
|
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
|
||||||
|
binder.validate(validationHints);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to raise a fatal bind exception on validation errors.
|
||||||
|
* @param binder the data binder used to perform data binding
|
||||||
|
* @param methodParam the method argument
|
||||||
* @return {@code true} if the next method argument is not of type {@link Errors}
|
* @return {@code true} if the next method argument is not of type {@link Errors}
|
||||||
* @since 4.1.5
|
* @since 4.1.5
|
||||||
*/
|
*/
|
||||||
protected boolean isBindingErrorFatal(MethodParameter parameter) {
|
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
|
||||||
int i = parameter.getParameterIndex();
|
int i = methodParam.getParameterIndex();
|
||||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
|
||||||
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||||
return !hasBindingResult;
|
return !hasBindingResult;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.web.servlet.mvc.method.annotation;
|
package org.springframework.web.servlet.mvc.method.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -26,14 +25,11 @@ import javax.servlet.http.Part;
|
||||||
|
|
||||||
import org.springframework.core.GenericCollectionTypeResolver;
|
import org.springframework.core.GenericCollectionTypeResolver;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.http.HttpInputMessage;
|
import org.springframework.http.HttpInputMessage;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.lang.UsesJava8;
|
import org.springframework.lang.UsesJava8;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.validation.Errors;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
@ -86,9 +82,9 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
/**
|
/**
|
||||||
* Supports the following:
|
* Supports the following:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Annotated with {@code @RequestPart}
|
* <li>annotated with {@code @RequestPart}
|
||||||
* <li>Of type {@link MultipartFile} unless annotated with {@code @RequestParam}.
|
* <li>of type {@link MultipartFile} unless annotated with {@code @RequestParam}
|
||||||
* <li>Of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}.
|
* <li>of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -164,7 +160,10 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
|
arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
|
||||||
WebDataBinder binder = binderFactory.createBinder(request, arg, partName);
|
WebDataBinder binder = binderFactory.createBinder(request, arg, partName);
|
||||||
if (arg != null) {
|
if (arg != null) {
|
||||||
validate(binder, parameter);
|
validateIfApplicable(binder, parameter);
|
||||||
|
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
|
||||||
|
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + partName, binder.getBindingResult());
|
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + partName, binder.getBindingResult());
|
||||||
}
|
}
|
||||||
|
|
@ -174,8 +173,8 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestPart annot = parameter.getParameterAnnotation(RequestPart.class);
|
RequestPart ann = parameter.getParameterAnnotation(RequestPart.class);
|
||||||
boolean isRequired = ((annot == null || annot.required()) && !optional);
|
boolean isRequired = ((ann == null || ann.required()) && !optional);
|
||||||
|
|
||||||
if (arg == null && isRequired) {
|
if (arg == null && isRequired) {
|
||||||
throw new MissingServletRequestPartException(partName);
|
throw new MissingServletRequestPartException(partName);
|
||||||
|
|
@ -194,44 +193,44 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPartName(MethodParameter param) {
|
private String getPartName(MethodParameter methodParam) {
|
||||||
RequestPart annot = param.getParameterAnnotation(RequestPart.class);
|
RequestPart ann = methodParam.getParameterAnnotation(RequestPart.class);
|
||||||
String partName = (annot != null ? annot.value() : "");
|
String partName = (ann != null ? ann.value() : "");
|
||||||
if (partName.length() == 0) {
|
if (partName.length() == 0) {
|
||||||
partName = param.getParameterName();
|
partName = methodParam.getParameterName();
|
||||||
if (partName == null) {
|
if (partName == null) {
|
||||||
throw new IllegalArgumentException("Request part name for argument type [" +
|
throw new IllegalArgumentException("Request part name for argument type [" +
|
||||||
param.getNestedParameterType().getName() +
|
methodParam.getNestedParameterType().getName() +
|
||||||
"] not specified, and parameter name information not found in class file either.");
|
"] not specified, and parameter name information not found in class file either.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return partName;
|
return partName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMultipartFileCollection(MethodParameter param) {
|
private boolean isMultipartFileCollection(MethodParameter methodParam) {
|
||||||
Class<?> collectionType = getCollectionParameterType(param);
|
Class<?> collectionType = getCollectionParameterType(methodParam);
|
||||||
return (collectionType != null && collectionType.equals(MultipartFile.class));
|
return MultipartFile.class.equals(collectionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMultipartFileArray(MethodParameter param) {
|
private boolean isMultipartFileArray(MethodParameter methodParam) {
|
||||||
Class<?> paramType = param.getNestedParameterType().getComponentType();
|
Class<?> paramType = methodParam.getNestedParameterType().getComponentType();
|
||||||
return (paramType != null && MultipartFile.class.equals(paramType));
|
return MultipartFile.class.equals(paramType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPartCollection(MethodParameter param) {
|
private boolean isPartCollection(MethodParameter methodParam) {
|
||||||
Class<?> collectionType = getCollectionParameterType(param);
|
Class<?> collectionType = getCollectionParameterType(methodParam);
|
||||||
return (collectionType != null && "javax.servlet.http.Part".equals(collectionType.getName()));
|
return (collectionType != null && "javax.servlet.http.Part".equals(collectionType.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPartArray(MethodParameter param) {
|
private boolean isPartArray(MethodParameter methodParam) {
|
||||||
Class<?> paramType = param.getNestedParameterType().getComponentType();
|
Class<?> paramType = methodParam.getNestedParameterType().getComponentType();
|
||||||
return (paramType != null && "javax.servlet.http.Part".equals(paramType.getName()));
|
return (paramType != null && "javax.servlet.http.Part".equals(paramType.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class<?> getCollectionParameterType(MethodParameter param) {
|
private Class<?> getCollectionParameterType(MethodParameter methodParam) {
|
||||||
Class<?> paramType = param.getNestedParameterType();
|
Class<?> paramType = methodParam.getNestedParameterType();
|
||||||
if (Collection.class.equals(paramType) || List.class.isAssignableFrom(paramType)){
|
if (Collection.class.equals(paramType) || List.class.isAssignableFrom(paramType)){
|
||||||
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(param);
|
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
|
||||||
if (valueType != null) {
|
if (valueType != null) {
|
||||||
return valueType;
|
return valueType;
|
||||||
}
|
}
|
||||||
|
|
@ -239,35 +238,6 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate the request part if applicable.
|
|
||||||
* <p>The default implementation checks for {@code @javax.validation.Valid},
|
|
||||||
* Spring's {@link org.springframework.validation.annotation.Validated},
|
|
||||||
* and custom annotations whose name starts with "Valid".
|
|
||||||
* @param binder the DataBinder to be used
|
|
||||||
* @param param the method parameter
|
|
||||||
* @throws MethodArgumentNotValidException in case of a binding error which
|
|
||||||
* is meant to be fatal (i.e. without a declared {@link Errors} parameter)
|
|
||||||
* @see #isBindingErrorFatal
|
|
||||||
*/
|
|
||||||
protected void validate(WebDataBinder binder, MethodParameter param) throws MethodArgumentNotValidException {
|
|
||||||
Annotation[] annotations = param.getParameterAnnotations();
|
|
||||||
for (Annotation ann : annotations) {
|
|
||||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
|
||||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
|
||||||
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
|
|
||||||
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
|
|
||||||
binder.validate(validationHints);
|
|
||||||
BindingResult bindingResult = binder.getBindingResult();
|
|
||||||
if (bindingResult.hasErrors()) {
|
|
||||||
if (isBindingErrorFatal(param)) {
|
|
||||||
throw new MethodArgumentNotValidException(param, bindingResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inner class to avoid hard-coded dependency on Servlet 3.0 Part type...
|
* Inner class to avoid hard-coded dependency on Servlet 3.0 Part type...
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PushbackInputStream;
|
import java.io.PushbackInputStream;
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
@ -32,8 +31,6 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||||
import org.springframework.http.server.ServletServerHttpRequest;
|
import org.springframework.http.server.ServletServerHttpRequest;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.validation.Errors;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||||
import org.springframework.web.accept.ContentNegotiationManager;
|
import org.springframework.web.accept.ContentNegotiationManager;
|
||||||
|
|
@ -47,16 +44,14 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves method arguments annotated with {@code @RequestBody} and handles
|
* Resolves method arguments annotated with {@code @RequestBody} and handles return
|
||||||
* return values from methods annotated with {@code @ResponseBody} by reading
|
* values from methods annotated with {@code @ResponseBody} by reading and writing
|
||||||
* and writing to the body of the request or response with an
|
* to the body of the request or response with an {@link HttpMessageConverter}.
|
||||||
* {@link HttpMessageConverter}.
|
|
||||||
*
|
*
|
||||||
* <p>An {@code @RequestBody} method argument is also validated if it is
|
* <p>An {@code @RequestBody} method argument is also validated if it is annotated
|
||||||
* annotated with {@code @javax.validation.Valid}. In case of validation
|
* with {@code @javax.validation.Valid}. In case of validation failure,
|
||||||
* failure, {@link MethodArgumentNotValidException} is raised and results
|
* {@link MethodArgumentNotValidException} is raised and results in a 400 response
|
||||||
* in a 400 response status code if {@link DefaultHandlerExceptionResolver}
|
* status code if {@link DefaultHandlerExceptionResolver} is configured.
|
||||||
* is configured.
|
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
|
@ -102,44 +97,17 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
||||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||||
|
|
||||||
Object argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
|
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
|
||||||
String name = Conventions.getVariableNameForParameter(parameter);
|
String name = Conventions.getVariableNameForParameter(parameter);
|
||||||
WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name);
|
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
|
||||||
if (argument != null) {
|
if (arg != null) {
|
||||||
validate(binder, parameter);
|
validateIfApplicable(binder, parameter);
|
||||||
}
|
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
|
||||||
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
|
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
|
||||||
return argument;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate the request part if applicable.
|
|
||||||
* <p>The default implementation checks for {@code @javax.validation.Valid},
|
|
||||||
* Spring's {@link org.springframework.validation.annotation.Validated},
|
|
||||||
* and custom annotations whose name starts with "Valid".
|
|
||||||
* @param binder the DataBinder to be used
|
|
||||||
* @param parameter the method parameter
|
|
||||||
* @throws MethodArgumentNotValidException in case of a binding error which
|
|
||||||
* is meant to be fatal (i.e. without a declared {@link Errors} parameter)
|
|
||||||
* @see #isBindingErrorFatal
|
|
||||||
*/
|
|
||||||
protected void validate(WebDataBinder binder, MethodParameter parameter) throws MethodArgumentNotValidException {
|
|
||||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
|
||||||
for (Annotation ann : annotations) {
|
|
||||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
|
||||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
|
||||||
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
|
|
||||||
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
|
|
||||||
binder.validate(validationHints);
|
|
||||||
BindingResult bindingResult = binder.getBindingResult();
|
|
||||||
if (bindingResult.hasErrors()) {
|
|
||||||
if (isBindingErrorFatal(parameter)) {
|
|
||||||
throw new MethodArgumentNotValidException(parameter, bindingResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
|
||||||
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2015 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.
|
||||||
|
|
@ -50,7 +50,7 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||||
/**
|
/**
|
||||||
* @param annotationNotRequired if "true", non-simple method arguments and
|
* @param annotationNotRequired if "true", non-simple method arguments and
|
||||||
* return values are considered model attributes with or without a
|
* return values are considered model attributes with or without a
|
||||||
* {@code @ModelAttribute} annotation.
|
* {@code @ModelAttribute} annotation
|
||||||
*/
|
*/
|
||||||
public ServletModelAttributeMethodProcessor(boolean annotationNotRequired) {
|
public ServletModelAttributeMethodProcessor(boolean annotationNotRequired) {
|
||||||
super(annotationNotRequired);
|
super(annotationNotRequired);
|
||||||
|
|
@ -62,21 +62,22 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||||
* request parameter if the name matches to the model attribute name and
|
* request parameter if the name matches to the model attribute name and
|
||||||
* if there is an appropriate type conversion strategy. If none of these
|
* if there is an appropriate type conversion strategy. If none of these
|
||||||
* are true delegate back to the base class.
|
* are true delegate back to the base class.
|
||||||
* @see #createAttributeFromRequestValue(String, String, MethodParameter, WebDataBinderFactory, NativeWebRequest)
|
* @see #createAttributeFromRequestValue
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected final Object createAttribute(String attributeName, MethodParameter parameter,
|
protected final Object createAttribute(String attributeName, MethodParameter methodParam,
|
||||||
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
|
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
|
||||||
|
|
||||||
String value = getRequestValueForAttribute(attributeName, request);
|
String value = getRequestValueForAttribute(attributeName, request);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
|
Object attribute = createAttributeFromRequestValue(
|
||||||
|
value, attributeName, methodParam, binderFactory, request);
|
||||||
if (attribute != null) {
|
if (attribute != null) {
|
||||||
return attribute;
|
return attribute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.createAttribute(attributeName, parameter, binderFactory, request);
|
return super.createAttribute(attributeName, methodParam, binderFactory, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -103,9 +104,8 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected final Map<String, String> getUriTemplateVariables(NativeWebRequest request) {
|
protected final Map<String, String> getUriTemplateVariables(NativeWebRequest request) {
|
||||||
Map<String, String> variables =
|
Map<String, String> variables = (Map<String, String>) request.getAttribute(
|
||||||
(Map<String, String>) request.getAttribute(
|
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||||
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
|
||||||
return (variables != null ? variables : Collections.<String, String>emptyMap());
|
return (variables != null ? variables : Collections.<String, String>emptyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,23 +116,23 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||||
* {@link Converter} that can perform the conversion.
|
* {@link Converter} that can perform the conversion.
|
||||||
* @param sourceValue the source value to create the model attribute from
|
* @param sourceValue the source value to create the model attribute from
|
||||||
* @param attributeName the name of the attribute, never {@code null}
|
* @param attributeName the name of the attribute, never {@code null}
|
||||||
* @param parameter the method parameter
|
* @param methodParam the method parameter
|
||||||
* @param binderFactory for creating WebDataBinder instance
|
* @param binderFactory for creating WebDataBinder instance
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @return the created model attribute, or {@code null}
|
* @return the created model attribute, or {@code null}
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected Object createAttributeFromRequestValue(String sourceValue, String attributeName,
|
protected Object createAttributeFromRequestValue(String sourceValue, String attributeName,
|
||||||
MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request)
|
MethodParameter methodParam, WebDataBinderFactory binderFactory, NativeWebRequest request)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
DataBinder binder = binderFactory.createBinder(request, null, attributeName);
|
DataBinder binder = binderFactory.createBinder(request, null, attributeName);
|
||||||
ConversionService conversionService = binder.getConversionService();
|
ConversionService conversionService = binder.getConversionService();
|
||||||
if (conversionService != null) {
|
if (conversionService != null) {
|
||||||
TypeDescriptor source = TypeDescriptor.valueOf(String.class);
|
TypeDescriptor source = TypeDescriptor.valueOf(String.class);
|
||||||
TypeDescriptor target = new TypeDescriptor(parameter);
|
TypeDescriptor target = new TypeDescriptor(methodParam);
|
||||||
if (conversionService.canConvert(source, target)) {
|
if (conversionService.canConvert(source, target)) {
|
||||||
return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
|
return binder.convertIfNecessary(sourceValue, methodParam.getParameterType(), methodParam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue