Browse Source

Revised validation javadoc

Issue: SPR-12655
pull/774/head
Juergen Hoeller 11 years ago
parent
commit
186fef6808
  1. 73
      spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java
  2. 65
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java
  3. 88
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java
  4. 77
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java
  5. 47
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletModelAttributeMethodProcessor.java

73
spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2015 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.
@ -21,6 +21,7 @@ import java.util.Map; @@ -21,6 +21,7 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
@ -54,10 +55,11 @@ import org.springframework.web.method.support.ModelAndViewContainer; @@ -54,10 +55,11 @@ import org.springframework.web.method.support.ModelAndViewContainer;
*/
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
protected Log logger = LogFactory.getLog(this.getClass());
protected final Log logger = LogFactory.getLog(getClass());
private final boolean annotationNotRequired;
/**
* @param annotationNotRequired if "true", non-simple method arguments and
* return values are considered model attributes with or without a
@ -67,10 +69,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol @@ -67,10 +69,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
this.annotationNotRequired = annotationNotRequired;
}
/**
* @return true if the parameter is annotated with {@link ModelAttribute}
* or in default resolution mode also if it is not a simple type.
* Returns {@code true} if the parameter is annotated with {@link ModelAttribute}
* or in default resolution mode, and also if it is not a simple type.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
return true;
@ -92,18 +96,16 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol @@ -92,18 +96,16 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
* and the next method parameter is not of type {@link Errors}.
* @throws Exception if WebDataBinder initialization fails.
*/
public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory)
throws Exception {
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String name = ModelFactory.getNameForParameter(parameter);
Object attribute = (mavContainer.containsAttribute(name)) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
Object attribute = (mavContainer.containsAttribute(name) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest));
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
bindRequestParameters(binder, request);
bindRequestParameters(binder, webRequest);
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
@ -113,7 +115,6 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol @@ -113,7 +115,6 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
@ -124,16 +125,16 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol @@ -124,16 +125,16 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
/**
* Extension point to create the model attribute if not found in the model.
* The default implementation uses the default constructor.
* @param attributeName the name of the attribute, never {@code null}
* @param parameter the method parameter
* @param attributeName the name of the attribute (never {@code null})
* @param methodParam the method parameter
* @param binderFactory for creating WebDataBinder instance
* @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,
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
protected Object createAttribute(String attributeName, MethodParameter methodParam,
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
return BeanUtils.instantiateClass(parameter.getParameterType());
return BeanUtils.instantiateClass(methodParam.getParameterType());
}
/**
@ -147,15 +148,17 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol @@ -147,15 +148,17 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
/**
* Validate the model attribute if applicable.
* <p>The default implementation checks for {@code @javax.validation.Valid}.
* <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
* @param methodParam the method parameter
*/
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation annot : annotations) {
if (annot.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(annot);
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
Annotation[] annotations = methodParam.getParameterAnnotations();
for (Annotation ann : annotations) {
if (ann.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(ann);
binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
break;
}
@ -165,14 +168,13 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol @@ -165,14 +168,13 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
/**
* Whether to raise a {@link BindException} on validation errors.
* @param binder the data binder used to perform data binding
* @param parameter the method argument
* @return {@code true} if the next method argument is not of type {@link Errors}.
* @param methodParam the method argument
* @return {@code true} if the next method argument is not of type {@link Errors}
*/
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
int i = parameter.getParameterIndex();
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
int i = methodParam.getParameterIndex();
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}
@ -195,14 +197,13 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol @@ -195,14 +197,13 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
/**
* Add non-null return values to the {@link ModelAndViewContainer}.
*/
public void handleReturnValue(
Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws Exception {
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue != null) {
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
mavContainer.addAttribute(name, returnValue);
}
}
}

65
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2015 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.
@ -25,11 +25,11 @@ import java.util.LinkedHashSet; @@ -25,11 +25,11 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
@ -59,12 +59,14 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements @@ -59,12 +59,14 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
protected final List<MediaType> allSupportedMediaTypes;
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
this.messageConverters = messageConverters;
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
}
/**
* Return the media types supported by all provided message converters sorted
* by specificity via {@link MediaType#sortBySpecificity(List)}.
@ -79,10 +81,10 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements @@ -79,10 +81,10 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
return Collections.unmodifiableList(result);
}
/**
* Creates the method argument value of the expected parameter type by
* Create the method argument value of the expected parameter type by
* reading from the given request.
*
* @param <T> the expected type of the argument value to be created
* @param webRequest the current request
* @param methodParam the method argument
@ -99,9 +101,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements @@ -99,9 +101,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
}
/**
* Creates the method argument value of the expected parameter type by reading
* Create the method argument value of the expected parameter type by reading
* from the given HttpInputMessage.
*
* @param <T> the expected type of the argument value to be created
* @param inputMessage the HTTP input message representing the current request
* @param methodParam the method argument
@ -123,43 +124,41 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements @@ -123,43 +124,41 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
Class<?> contextClass = methodParam.getDeclaringClass();
Map<TypeVariable, Type> map = GenericTypeResolver.getTypeVariableMap(contextClass);
Class<T> targetClass = (Class<T>) GenericTypeResolver.resolveType(targetType, map);
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetType + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return genericConverter.read(targetType, contextClass, inputMessage);
}
Class<?> contextClass = methodParam.getDeclaringClass();
Map<TypeVariable, Type> map = GenericTypeResolver.getTypeVariableMap(contextClass);
Class<T> targetClass = (Class<T>) GenericTypeResolver.resolveType(targetType, map);
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetType + "] as \"" +
contentType + "\" using [" + converter + "]");
}
if (targetClass != null) {
if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
}
return genericConverter.read(targetType, contextClass, inputMessage);
}
}
if (targetClass != null) {
if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
}
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
}
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}
/**
* Creates a new {@link HttpInputMessage} from the given {@link NativeWebRequest}.
*
* Create a new {@link HttpInputMessage} from the given {@link NativeWebRequest}.
* @param webRequest the web request to create an input message from
* @return the input message
*/

88
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2015 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.
@ -49,18 +49,15 @@ import org.springframework.web.util.WebUtils; @@ -49,18 +49,15 @@ import org.springframework.web.util.WebUtils;
/**
* Resolves the following method arguments:
* <ul>
* <li>Annotated with {@code @RequestPart}
* <li>Of type {@link MultipartFile} in conjunction with Spring's
* {@link MultipartResolver} abstraction
* <li>Of type {@code javax.servlet.http.Part} in conjunction with
* Servlet 3.0 multipart requests
* <li>Annotated with {@code @RequestPart}
* <li>Of type {@link MultipartFile} in conjunction with Spring's {@link MultipartResolver} abstraction
* <li>Of type {@code javax.servlet.http.Part} in conjunction with Servlet 3.0 multipart requests
* </ul>
*
* <p>When a parameter is annotated with {@code @RequestPart} the content of the
* part is passed through an {@link HttpMessageConverter} to resolve the method
* argument with the 'Content-Type' of the request part in mind. This is
* analogous to what @{@link RequestBody} does to resolve an argument based on
* the content of a regular request.
* <p>When a parameter is annotated with {@code @RequestPart}, the content of the part is
* passed through an {@link HttpMessageConverter} to resolve the method argument with the
* 'Content-Type' of the request part in mind. This is analogous to what @{@link RequestBody}
* does to resolve an argument based on the content of a regular request.
*
* <p>When a parameter is not annotated or the name of the part is not specified,
* it is derived from the name of the method argument.
@ -82,9 +79,9 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM @@ -82,9 +79,9 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
/**
* Supports the following:
* <ul>
* <li>Annotated with {@code @RequestPart}
* <li>Of type {@link MultipartFile} unless annotated with {@code @RequestParam}.
* <li>Of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}.
* <li>annotated with {@code @RequestPart}
* <li>of type {@link MultipartFile} unless annotated with {@code @RequestParam}
* <li>of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}
* </ul>
*/
public boolean supportsParameter(MethodParameter parameter) {
@ -146,8 +143,8 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM @@ -146,8 +143,8 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
}
}
RequestPart annot = parameter.getParameterAnnotation(RequestPart.class);
boolean isRequired = (annot == null || annot.required());
RequestPart ann = parameter.getParameterAnnotation(RequestPart.class);
boolean isRequired = (ann == null || ann.required());
if (arg == null && isRequired) {
throw new MissingServletRequestPartException(partName);
@ -163,39 +160,51 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM @@ -163,39 +160,51 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
}
}
private String getPartName(MethodParameter parameter) {
RequestPart annot = parameter.getParameterAnnotation(RequestPart.class);
String partName = (annot != null) ? annot.value() : "";
private String getPartName(MethodParameter methodParam) {
RequestPart annot = methodParam.getParameterAnnotation(RequestPart.class);
String partName = (annot != null ? annot.value() : "");
if (partName.length() == 0) {
partName = parameter.getParameterName();
Assert.notNull(partName, "Request part name for argument type [" + parameter.getParameterType().getName()
+ "] not available, and parameter name information not found in class file either.");
partName = methodParam.getParameterName();
if (partName == null) {
throw new IllegalArgumentException("Request part name for argument type [" +
methodParam.getNestedParameterType().getName() +
"] not specified, and parameter name information not found in class file either.");
}
}
return partName;
}
private boolean isMultipartFileCollection(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
private boolean isMultipartFileCollection(MethodParameter methodParam) {
Class<?> paramType = methodParam.getParameterType();
if (Collection.class.equals(paramType) || List.class.isAssignableFrom(paramType)){
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(parameter);
if (valueType != null && valueType.equals(MultipartFile.class)) {
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
if (MultipartFile.class.equals(valueType)) {
return true;
}
}
return false;
}
private void validate(WebDataBinder binder, MethodParameter parameter) throws MethodArgumentNotValidException {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation annot : annotations) {
if (annot.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(annot);
/**
* 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 methodParam 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)
*/
private void validate(WebDataBinder binder, MethodParameter methodParam) throws MethodArgumentNotValidException {
Annotation[] annotations = methodParam.getParameterAnnotations();
for (Annotation ann : annotations) {
if (ann.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(ann);
binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
BindingResult bindingResult = binder.getBindingResult();
if (bindingResult.hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, bindingResult);
if (isBindExceptionRequired(binder, methodParam)) {
throw new MethodArgumentNotValidException(methodParam, bindingResult);
}
}
}
@ -205,14 +214,13 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM @@ -205,14 +214,13 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
/**
* Whether to raise a {@link MethodArgumentNotValidException} on validation errors.
* @param binder the data binder used to perform data binding
* @param parameter the method argument
* @return {@code true} if the next method argument is not of type {@link Errors}.
* @param methodParam the method argument
* @return {@code true} if the next method argument is not of type {@link Errors}
*/
private boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
int i = parameter.getParameterIndex();
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
private boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
int i = methodParam.getParameterIndex();
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}

77
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2015 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.
@ -22,7 +22,6 @@ import java.io.PushbackInputStream; @@ -22,7 +22,6 @@ import java.io.PushbackInputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.Conventions;
@ -47,16 +46,14 @@ import org.springframework.web.method.support.ModelAndViewContainer; @@ -47,16 +46,14 @@ import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
/**
* Resolves method arguments annotated with {@code @RequestBody} and handles
* return values from methods annotated with {@code @ResponseBody} by reading
* and writing to the body of the request or response with an
* {@link HttpMessageConverter}.
* Resolves method arguments annotated with {@code @RequestBody} and handles return
* values from methods annotated with {@code @ResponseBody} by reading and writing
* to the body of the request or response with an {@link HttpMessageConverter}.
*
* <p>An {@code @RequestBody} method argument is also validated if it is
* annotated with {@code @javax.validation.Valid}. In case of validation
* failure, {@link MethodArgumentNotValidException} is raised and results
* in a 400 response status code if {@link DefaultHandlerExceptionResolver}
* is configured.
* <p>An {@code @RequestBody} method argument is also validated if it is annotated
* with {@code @javax.validation.Valid}. In case of validation failure,
* {@link MethodArgumentNotValidException} is raised and results in a 400 response
* status code if {@link DefaultHandlerExceptionResolver} is configured.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
@ -74,49 +71,54 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter @@ -74,49 +71,54 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
super(messageConverters, contentNegotiationManager);
}
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
public boolean supportsReturnType(MethodParameter returnType) {
return returnType.getMethodAnnotation(ResponseBody.class) != null;
return (returnType.getMethodAnnotation(ResponseBody.class) != null);
}
/**
* {@inheritDoc}
* @throws MethodArgumentNotValidException if validation fails
* Throws MethodArgumentNotValidException if validation fails.
* @throws HttpMessageNotReadableException if {@link RequestBody#required()}
* is {@code true} and there is no body content or if there is no suitable
* converter to read the content with.
* is {@code true} and there is no body content or if there is no suitable
* converter to read the content with.
*/
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Object argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name);
if (argument != null) {
validate(binder, parameter);
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return argument;
}
private void validate(WebDataBinder binder, MethodParameter parameter) throws Exception, MethodArgumentNotValidException {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation annot : annotations) {
if (annot.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(annot);
/**
* 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 methodParam 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)
*/
private void validate(WebDataBinder binder, MethodParameter methodParam) throws MethodArgumentNotValidException {
Annotation[] annotations = methodParam.getParameterAnnotations();
for (Annotation ann : annotations) {
if (ann.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(ann);
binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
BindingResult bindingResult = binder.getBindingResult();
if (bindingResult.hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, bindingResult);
if (isBindExceptionRequired(binder, methodParam)) {
throw new MethodArgumentNotValidException(methodParam, bindingResult);
}
}
break;
@ -127,26 +129,25 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter @@ -127,26 +129,25 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
/**
* Whether to raise a {@link MethodArgumentNotValidException} on validation errors.
* @param binder the data binder used to perform data binding
* @param parameter the method argument
* @return {@code true} if the next method argument is not of type {@link Errors}.
* @param methodParam the method argument
* @return {@code true} if the next method argument is not of type {@link Errors}
*/
private boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
int i = parameter.getParameterIndex();
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
private boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
int i = methodParam.getParameterIndex();
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest,
MethodParameter methodParam, Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter methodParam,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
final HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
HttpInputMessage inputMessage = new ServletServerHttpRequest(servletRequest);
RequestBody annot = methodParam.getParameterAnnotation(RequestBody.class);
if (!annot.required()) {
RequestBody ann = methodParam.getParameterAnnotation(RequestBody.class);
if (!ann.required()) {
InputStream inputStream = inputMessage.getBody();
if (inputStream == null) {
return null;

47
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletModelAttributeMethodProcessor.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2015 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.
@ -18,7 +18,6 @@ package org.springframework.web.servlet.mvc.method.annotation; @@ -18,7 +18,6 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.util.Collections;
import java.util.Map;
import javax.servlet.ServletRequest;
import org.springframework.core.MethodParameter;
@ -36,7 +35,7 @@ import org.springframework.web.method.annotation.ModelAttributeMethodProcessor; @@ -36,7 +35,7 @@ import org.springframework.web.method.annotation.ModelAttributeMethodProcessor;
import org.springframework.web.servlet.HandlerMapping;
/**
* A Servlet-specific {@link org.springframework.web.method.annotation.ModelAttributeMethodProcessor} that applies data
* A Servlet-specific {@link ModelAttributeMethodProcessor} that applies data
* binding through a WebDataBinder of type {@link ServletRequestDataBinder}.
*
* <p>Also adds a fall-back strategy to instantiate the model attribute from a
@ -51,34 +50,34 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr @@ -51,34 +50,34 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
/**
* @param annotationNotRequired if "true", non-simple method arguments and
* return values are considered model attributes with or without a
* {@code @ModelAttribute} annotation.
* {@code @ModelAttribute} annotation
*/
public ServletModelAttributeMethodProcessor(boolean annotationNotRequired) {
super(annotationNotRequired);
}
/**
* Instantiate the model attribute from a URI template variable or from a
* request parameter if the name matches to the model attribute name and
* if there is an appropriate type conversion strategy. If none of these
* are true delegate back to the base class.
* @see #createAttributeFromRequestValue(String, String, MethodParameter, WebDataBinderFactory, NativeWebRequest)
* @see #createAttributeFromRequestValue
*/
@Override
protected final Object createAttribute(String attributeName,
MethodParameter parameter,
WebDataBinderFactory binderFactory,
NativeWebRequest request) throws Exception {
protected final Object createAttribute(String attributeName, MethodParameter methodParam,
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
String value = getRequestValueForAttribute(attributeName, request);
if (value != null) {
Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
Object attribute = createAttributeFromRequestValue(
value, attributeName, methodParam, binderFactory, request);
if (attribute != null) {
return attribute;
}
}
return super.createAttribute(attributeName, parameter, binderFactory, request);
return super.createAttribute(attributeName, methodParam, binderFactory, request);
}
/**
@ -105,10 +104,9 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr @@ -105,10 +104,9 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
@SuppressWarnings("unchecked")
protected final Map<String, String> getUriTemplateVariables(NativeWebRequest request) {
Map<String, String> variables =
(Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return (variables != null) ? variables : Collections.<String, String>emptyMap();
Map<String, String> variables = (Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return (variables != null ? variables : Collections.<String, String>emptyMap());
}
/**
@ -118,32 +116,31 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr @@ -118,32 +116,31 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
* {@link Converter} that can perform the conversion.
* @param sourceValue the source value to create the model attribute from
* @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 request the current request
* @return the created model attribute, or {@code null}
* @throws Exception
*/
protected Object createAttributeFromRequestValue(String sourceValue,
String attributeName,
MethodParameter parameter,
WebDataBinderFactory binderFactory,
NativeWebRequest request) throws Exception {
protected Object createAttributeFromRequestValue(String sourceValue, String attributeName,
MethodParameter methodParam, WebDataBinderFactory binderFactory, NativeWebRequest request)
throws Exception {
DataBinder binder = binderFactory.createBinder(request, null, attributeName);
ConversionService conversionService = binder.getConversionService();
if (conversionService != null) {
TypeDescriptor source = TypeDescriptor.valueOf(String.class);
TypeDescriptor target = new TypeDescriptor(parameter);
TypeDescriptor target = new TypeDescriptor(methodParam);
if (conversionService.canConvert(source, target)) {
return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
return binder.convertIfNecessary(sourceValue, methodParam.getParameterType(), methodParam);
}
}
return null;
}
/**
* {@inheritDoc}
* <p>Downcast {@link WebDataBinder} to {@link ServletRequestDataBinder} before binding.
* This implementation downcasts {@link WebDataBinder} to
* {@link ServletRequestDataBinder} before binding.
* @see ServletRequestDataBinderFactory
*/
@Override

Loading…
Cancel
Save