Revised common validation methods in AbstractMessageConverterMethodArgumentResolver

The protected validation methods are analogous to ModelAttributeMethodProcessor now.

Issue: SPR-12655
This commit is contained in:
Juergen Hoeller 2015-02-19 23:37:07 +01:00
parent 65b6017db9
commit 7191050e26
5 changed files with 105 additions and 143 deletions

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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...

View File

@ -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

View File

@ -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;