Skip to content

Commit 7191050

Browse files
committed
Revised common validation methods in AbstractMessageConverterMethodArgumentResolver
The protected validation methods are analogous to ModelAttributeMethodProcessor now. Issue: SPR-12655
1 parent 65b6017 commit 7191050

File tree

5 files changed

+105
-143
lines changed

5 files changed

+105
-143
lines changed

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

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,12 @@ public final Object resolveArgument(MethodParameter parameter, ModelAndViewConta
109109
if (binder.getTarget() != null) {
110110
bindRequestParameters(binder, webRequest);
111111
validateIfApplicable(binder, parameter);
112-
if (binder.getBindingResult().hasErrors()) {
113-
if (isBindExceptionRequired(binder, parameter)) {
114-
throw new BindException(binder.getBindingResult());
115-
}
112+
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
113+
throw new BindException(binder.getBindingResult());
116114
}
117115
}
118116

119117
// Add resolved attribute and BindingResult at the end of the model
120-
121118
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
122119
mavContainer.removeAttributes(bindingResultModel);
123120
mavContainer.addAllAttributes(bindingResultModel);
@@ -129,15 +126,15 @@ public final Object resolveArgument(MethodParameter parameter, ModelAndViewConta
129126
* Extension point to create the model attribute if not found in the model.
130127
* The default implementation uses the default constructor.
131128
* @param attributeName the name of the attribute (never {@code null})
132-
* @param parameter the method parameter
129+
* @param methodParam the method parameter
133130
* @param binderFactory for creating WebDataBinder instance
134131
* @param request the current request
135132
* @return the created model attribute (never {@code null})
136133
*/
137-
protected Object createAttribute(String attributeName, MethodParameter parameter,
138-
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
134+
protected Object createAttribute(String attributeName, MethodParameter methodParam,
135+
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
139136

140-
return BeanUtils.instantiateClass(parameter.getParameterType());
137+
return BeanUtils.instantiateClass(methodParam.getParameterType());
141138
}
142139

143140
/**
@@ -155,10 +152,10 @@ protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest requ
155152
* Spring's {@link org.springframework.validation.annotation.Validated},
156153
* and custom annotations whose name starts with "Valid".
157154
* @param binder the DataBinder to be used
158-
* @param parameter the method parameter
155+
* @param methodParam the method parameter
159156
*/
160-
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
161-
Annotation[] annotations = parameter.getParameterAnnotations();
157+
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
158+
Annotation[] annotations = methodParam.getParameterAnnotations();
162159
for (Annotation ann : annotations) {
163160
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
164161
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
@@ -171,16 +168,15 @@ protected void validateIfApplicable(WebDataBinder binder, MethodParameter parame
171168
}
172169

173170
/**
174-
* Whether to raise a {@link BindException} on validation errors.
171+
* Whether to raise a fatal bind exception on validation errors.
175172
* @param binder the data binder used to perform data binding
176-
* @param parameter the method argument
177-
* @return {@code true} if the next method argument is not of type {@link Errors}.
173+
* @param methodParam the method argument
174+
* @return {@code true} if the next method argument is not of type {@link Errors}
178175
*/
179-
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
180-
int i = parameter.getParameterIndex();
181-
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
176+
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
177+
int i = methodParam.getParameterIndex();
178+
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
182179
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
183-
184180
return !hasBindingResult;
185181
}
186182

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

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.web.servlet.mvc.method.annotation;
1818

1919
import java.io.IOException;
20+
import java.lang.annotation.Annotation;
2021
import java.lang.reflect.Type;
2122
import java.util.ArrayList;
2223
import java.util.Collections;
@@ -30,6 +31,7 @@
3031

3132
import org.springframework.core.MethodParameter;
3233
import org.springframework.core.ResolvableType;
34+
import org.springframework.core.annotation.AnnotationUtils;
3335
import org.springframework.http.HttpInputMessage;
3436
import org.springframework.http.InvalidMediaTypeException;
3537
import org.springframework.http.MediaType;
@@ -38,7 +40,9 @@
3840
import org.springframework.http.server.ServletServerHttpRequest;
3941
import org.springframework.util.Assert;
4042
import org.springframework.validation.Errors;
43+
import org.springframework.validation.annotation.Validated;
4144
import org.springframework.web.HttpMediaTypeNotSupportedException;
45+
import org.springframework.web.bind.WebDataBinder;
4246
import org.springframework.web.context.request.NativeWebRequest;
4347
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
4448

@@ -113,8 +117,8 @@ protected <T> Object readWithMessageConverters(NativeWebRequest webRequest,
113117
* @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
114118
*/
115119
@SuppressWarnings("unchecked")
116-
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter methodParam,
117-
Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
120+
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
121+
MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
118122

119123
MediaType contentType;
120124
try {
@@ -171,14 +175,38 @@ protected ServletServerHttpRequest createInputMessage(NativeWebRequest webReques
171175
}
172176

173177
/**
174-
* Whether to raise a handler method invocation exception on validation errors.
175-
* @param parameter the method argument
178+
* Validate the request part if applicable.
179+
* <p>The default implementation checks for {@code @javax.validation.Valid},
180+
* Spring's {@link org.springframework.validation.annotation.Validated},
181+
* and custom annotations whose name starts with "Valid".
182+
* @param binder the DataBinder to be used
183+
* @param methodParam the method parameter
184+
* @see #isBindExceptionRequired
185+
* @since 4.1.5
186+
*/
187+
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
188+
Annotation[] annotations = methodParam.getParameterAnnotations();
189+
for (Annotation ann : annotations) {
190+
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
191+
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
192+
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
193+
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
194+
binder.validate(validationHints);
195+
break;
196+
}
197+
}
198+
}
199+
200+
/**
201+
* Whether to raise a fatal bind exception on validation errors.
202+
* @param binder the data binder used to perform data binding
203+
* @param methodParam the method argument
176204
* @return {@code true} if the next method argument is not of type {@link Errors}
177205
* @since 4.1.5
178206
*/
179-
protected boolean isBindingErrorFatal(MethodParameter parameter) {
180-
int i = parameter.getParameterIndex();
181-
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
207+
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
208+
int i = methodParam.getParameterIndex();
209+
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
182210
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
183211
return !hasBindingResult;
184212
}

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

Lines changed: 27 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.web.servlet.mvc.method.annotation;
1818

19-
import java.lang.annotation.Annotation;
2019
import java.util.ArrayList;
2120
import java.util.Collection;
2221
import java.util.List;
@@ -26,14 +25,11 @@
2625

2726
import org.springframework.core.GenericCollectionTypeResolver;
2827
import org.springframework.core.MethodParameter;
29-
import org.springframework.core.annotation.AnnotationUtils;
3028
import org.springframework.http.HttpInputMessage;
3129
import org.springframework.http.converter.HttpMessageConverter;
3230
import org.springframework.lang.UsesJava8;
3331
import org.springframework.util.Assert;
3432
import org.springframework.validation.BindingResult;
35-
import org.springframework.validation.Errors;
36-
import org.springframework.validation.annotation.Validated;
3733
import org.springframework.web.bind.MethodArgumentNotValidException;
3834
import org.springframework.web.bind.WebDataBinder;
3935
import org.springframework.web.bind.annotation.RequestBody;
@@ -86,9 +82,9 @@ public RequestPartMethodArgumentResolver(List<HttpMessageConverter<?>> messageCo
8682
/**
8783
* Supports the following:
8884
* <ul>
89-
* <li>Annotated with {@code @RequestPart}
90-
* <li>Of type {@link MultipartFile} unless annotated with {@code @RequestParam}.
91-
* <li>Of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}.
85+
* <li>annotated with {@code @RequestPart}
86+
* <li>of type {@link MultipartFile} unless annotated with {@code @RequestParam}
87+
* <li>of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}
9288
* </ul>
9389
*/
9490
@Override
@@ -164,7 +160,10 @@ else if (isPartArray(parameter)) {
164160
arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
165161
WebDataBinder binder = binderFactory.createBinder(request, arg, partName);
166162
if (arg != null) {
167-
validate(binder, parameter);
163+
validateIfApplicable(binder, parameter);
164+
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
165+
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
166+
}
168167
}
169168
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + partName, binder.getBindingResult());
170169
}
@@ -174,8 +173,8 @@ else if (isPartArray(parameter)) {
174173
}
175174
}
176175

177-
RequestPart annot = parameter.getParameterAnnotation(RequestPart.class);
178-
boolean isRequired = ((annot == null || annot.required()) && !optional);
176+
RequestPart ann = parameter.getParameterAnnotation(RequestPart.class);
177+
boolean isRequired = ((ann == null || ann.required()) && !optional);
179178

180179
if (arg == null && isRequired) {
181180
throw new MissingServletRequestPartException(partName);
@@ -194,80 +193,51 @@ private static void assertIsMultipartRequest(HttpServletRequest request) {
194193
}
195194
}
196195

197-
private String getPartName(MethodParameter param) {
198-
RequestPart annot = param.getParameterAnnotation(RequestPart.class);
199-
String partName = (annot != null ? annot.value() : "");
196+
private String getPartName(MethodParameter methodParam) {
197+
RequestPart ann = methodParam.getParameterAnnotation(RequestPart.class);
198+
String partName = (ann != null ? ann.value() : "");
200199
if (partName.length() == 0) {
201-
partName = param.getParameterName();
200+
partName = methodParam.getParameterName();
202201
if (partName == null) {
203202
throw new IllegalArgumentException("Request part name for argument type [" +
204-
param.getNestedParameterType().getName() +
203+
methodParam.getNestedParameterType().getName() +
205204
"] not specified, and parameter name information not found in class file either.");
206205
}
207206
}
208207
return partName;
209208
}
210209

211-
private boolean isMultipartFileCollection(MethodParameter param) {
212-
Class<?> collectionType = getCollectionParameterType(param);
213-
return (collectionType != null && collectionType.equals(MultipartFile.class));
210+
private boolean isMultipartFileCollection(MethodParameter methodParam) {
211+
Class<?> collectionType = getCollectionParameterType(methodParam);
212+
return MultipartFile.class.equals(collectionType);
214213
}
215214

216-
private boolean isMultipartFileArray(MethodParameter param) {
217-
Class<?> paramType = param.getNestedParameterType().getComponentType();
218-
return (paramType != null && MultipartFile.class.equals(paramType));
215+
private boolean isMultipartFileArray(MethodParameter methodParam) {
216+
Class<?> paramType = methodParam.getNestedParameterType().getComponentType();
217+
return MultipartFile.class.equals(paramType);
219218
}
220219

221-
private boolean isPartCollection(MethodParameter param) {
222-
Class<?> collectionType = getCollectionParameterType(param);
220+
private boolean isPartCollection(MethodParameter methodParam) {
221+
Class<?> collectionType = getCollectionParameterType(methodParam);
223222
return (collectionType != null && "javax.servlet.http.Part".equals(collectionType.getName()));
224223
}
225224

226-
private boolean isPartArray(MethodParameter param) {
227-
Class<?> paramType = param.getNestedParameterType().getComponentType();
225+
private boolean isPartArray(MethodParameter methodParam) {
226+
Class<?> paramType = methodParam.getNestedParameterType().getComponentType();
228227
return (paramType != null && "javax.servlet.http.Part".equals(paramType.getName()));
229228
}
230229

231-
private Class<?> getCollectionParameterType(MethodParameter param) {
232-
Class<?> paramType = param.getNestedParameterType();
230+
private Class<?> getCollectionParameterType(MethodParameter methodParam) {
231+
Class<?> paramType = methodParam.getNestedParameterType();
233232
if (Collection.class.equals(paramType) || List.class.isAssignableFrom(paramType)){
234-
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(param);
233+
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
235234
if (valueType != null) {
236235
return valueType;
237236
}
238237
}
239238
return null;
240239
}
241240

242-
/**
243-
* Validate the request part if applicable.
244-
* <p>The default implementation checks for {@code @javax.validation.Valid},
245-
* Spring's {@link org.springframework.validation.annotation.Validated},
246-
* and custom annotations whose name starts with "Valid".
247-
* @param binder the DataBinder to be used
248-
* @param param the method parameter
249-
* @throws MethodArgumentNotValidException in case of a binding error which
250-
* is meant to be fatal (i.e. without a declared {@link Errors} parameter)
251-
* @see #isBindingErrorFatal
252-
*/
253-
protected void validate(WebDataBinder binder, MethodParameter param) throws MethodArgumentNotValidException {
254-
Annotation[] annotations = param.getParameterAnnotations();
255-
for (Annotation ann : annotations) {
256-
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
257-
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
258-
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
259-
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
260-
binder.validate(validationHints);
261-
BindingResult bindingResult = binder.getBindingResult();
262-
if (bindingResult.hasErrors()) {
263-
if (isBindingErrorFatal(param)) {
264-
throw new MethodArgumentNotValidException(param, bindingResult);
265-
}
266-
}
267-
}
268-
}
269-
}
270-
271241

272242
/**
273243
* Inner class to avoid hard-coded dependency on Servlet 3.0 Part type...

0 commit comments

Comments
 (0)