diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestBodyNotValidException.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestBodyNotValidException.java deleted file mode 100644 index 3ebdebd2bb..0000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestBodyNotValidException.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2002-2011 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.web.servlet.mvc.method.annotation.support; - -import org.springframework.validation.Errors; -import org.springframework.validation.ObjectError; -import org.springframework.web.bind.annotation.RequestBody; - -/** - * Thrown by {@link RequestResponseBodyMethodProcessor} when an @{@link RequestBody} argument annotated with - * {@code @Valid} results in validation errors. - * - * @author Rossen Stoyanchev - * @since 3.1 - */ -@SuppressWarnings("serial") -public class RequestBodyNotValidException extends RuntimeException { - - private final Errors errors; - - /** - * @param errors contains the results of validating an @{@link RequestBody} argument. - */ - public RequestBodyNotValidException(Errors errors) { - this.errors = errors; - } - - /** - * Returns an Errors instance with validation errors. - */ - public Errors getErrors() { - return errors; - } - - @Override - public String getMessage() { - StringBuilder sb = new StringBuilder("Request body content validation failed: "); - sb.append(errors.getErrorCount()).append(" errors"); - for (ObjectError error : errors.getAllErrors()) { - sb.append('\n').append(error); - } - return sb.toString(); - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java index 73c594ab19..a5eb2e738e 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java @@ -27,7 +27,7 @@ import org.springframework.core.MethodParameter; import org.springframework.http.HttpInputMessage; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.Assert; -import org.springframework.validation.Errors; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.RequestBody; @@ -35,6 +35,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.annotation.support.MethodArgumentNotValidException; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; @@ -134,9 +135,9 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM if (isValidationApplicable(arg, parameter)) { WebDataBinder binder = binderFactory.createBinder(request, arg, partName); binder.validate(); - Errors errors = binder.getBindingResult(); - if (errors.hasErrors()) { - throw new RequestPartNotValidException(errors); + BindingResult bindingResult = binder.getBindingResult(); + if (bindingResult.hasErrors()) { + throw new MethodArgumentNotValidException(parameter, bindingResult); } } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartNotValidException.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartNotValidException.java deleted file mode 100644 index 2d2f782e85..0000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartNotValidException.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2002-2011 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.web.servlet.mvc.method.annotation.support; - -import org.springframework.validation.Errors; -import org.springframework.validation.ObjectError; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestPart; - -/** - * Thrown by {@link RequestPartMethodArgumentResolver} when an @{@link RequestPart} argument also annotated with - * {@code @Valid} results in validation errors. - * - * @author Rossen Stoyanchev - * @since 3.1 - */ -@SuppressWarnings("serial") -public class RequestPartNotValidException extends RuntimeException { - - private final Errors errors; - - /** - * @param errors contains the results of validating an @{@link RequestBody} argument. - */ - public RequestPartNotValidException(Errors errors) { - this.errors = errors; - } - - /** - * Returns an Errors instance with validation errors. - */ - public Errors getErrors() { - return errors; - } - - @Override - public String getMessage() { - StringBuilder sb = new StringBuilder( - "Validation of the content of request part '" + errors.getObjectName() + "' failed: "); - sb.append(errors.getErrorCount()).append(" errors"); - for (ObjectError error : errors.getAllErrors()) { - sb.append('\n').append(error); - } - return sb.toString(); - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java index 20fbdc73d7..2ef1020f67 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java @@ -23,13 +23,14 @@ import java.util.List; import org.springframework.core.Conventions; import org.springframework.core.MethodParameter; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.validation.Errors; +import org.springframework.validation.BindingResult; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.annotation.support.MethodArgumentNotValidException; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; @@ -38,7 +39,7 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv * annotated with {@link ResponseBody}. * *
An @{@link RequestBody} method argument will be validated if annotated with {@code @Valid}. - * In case of validation failure, a {@link RequestBodyNotValidException} is thrown and handled + * In case of validation failure, a {@link MethodArgumentNotValidException} is thrown and handled * automatically in {@link DefaultHandlerExceptionResolver}. * * @author Arjen Poutsma @@ -68,9 +69,9 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter String name = Conventions.getVariableNameForParameter(parameter); WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); binder.validate(); - Errors errors = binder.getBindingResult(); - if (errors.hasErrors()) { - throw new RequestBodyNotValidException(errors); + BindingResult bindingResult = binder.getBindingResult(); + if (bindingResult.hasErrors()) { + throw new MethodArgumentNotValidException(parameter, bindingResult); } } return arg; diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java index dc87a77bec..d5958069de 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java @@ -37,10 +37,11 @@ import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.method.annotation.support.MethodArgumentNotValidException; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver; -import org.springframework.web.servlet.mvc.method.annotation.support.RequestBodyNotValidException; -import org.springframework.web.servlet.mvc.method.annotation.support.RequestPartNotValidException; import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; /** @@ -127,11 +128,8 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes else if (ex instanceof HttpMessageNotWritableException) { return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler); } - else if (ex instanceof RequestBodyNotValidException) { - return handleRequestBodyNotValidException((RequestBodyNotValidException) ex, request, response, handler); - } - else if (ex instanceof RequestPartNotValidException) { - return handleRequestPartNotValidException((RequestPartNotValidException) ex, request, response, handler); + else if (ex instanceof MethodArgumentNotValidException) { + return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response, handler); } } catch (Exception handlerException) { @@ -343,33 +341,19 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes } /** - * Handle the case where the object created from the body of a request has failed validation. - * The default implementation sends an HTTP 400 error. + * Handle the case where an argument annotated with {@code @Valid} such as + * an {@link RequestBody} or {@link RequestPart} argument fails validation. + * An HTTP 400 error is sent back to the client. * @param request current HTTP request * @param response current HTTP response * @param handler the executed handler * @return an empty ModelAndView indicating the exception was handled * @throws IOException potentially thrown from response.sendError() */ - protected ModelAndView handleRequestBodyNotValidException(RequestBodyNotValidException ex, + protected ModelAndView handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(HttpServletResponse.SC_BAD_REQUEST); return new ModelAndView(); } - /** - * Handle the case where the object created from the part of a multipart request has failed validation. - * The default implementation sends an HTTP 400 error. - * @param request current HTTP request - * @param response current HTTP response - * @param handler the executed handler - * @return an empty ModelAndView indicating the exception was handled - * @throws IOException potentially thrown from response.sendError() - */ - protected ModelAndView handleRequestPartNotValidException(RequestPartNotValidException ex, - HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return new ModelAndView(); - } - } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java index ecf860f8f3..705c499a3b 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java @@ -59,6 +59,7 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.method.annotation.support.MethodArgumentNotValidException; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.support.RequestPartServletServerHttpRequest; @@ -194,10 +195,10 @@ public class RequestPartMethodArgumentResolverTests { try { testResolveArgument(new SimpleBean(null), paramValidRequestPart); fail("Expected exception"); - } catch (RequestPartNotValidException e) { - assertEquals("requestPart", e.getErrors().getObjectName()); - assertEquals(1, e.getErrors().getErrorCount()); - assertNotNull(e.getErrors().getFieldError("name")); + } catch (MethodArgumentNotValidException e) { + assertEquals("requestPart", e.getBindingResult().getObjectName()); + assertEquals(1, e.getBindingResult().getErrorCount()); + assertNotNull(e.getBindingResult().getFieldError("name")); } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java index e96f19e6b4..e7e2712fef 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java @@ -59,6 +59,7 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.method.annotation.support.MethodArgumentNotValidException; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.HandlerMapping; @@ -148,10 +149,10 @@ public class RequestResponseBodyMethodProcessorTests { try { testResolveArgumentWithValidation(new SimpleBean(null)); fail("Expected exception"); - } catch (RequestBodyNotValidException e) { - assertEquals("simpleBean", e.getErrors().getObjectName()); - assertEquals(1, e.getErrors().getErrorCount()); - assertNotNull(e.getErrors().getFieldError("name")); + } catch (MethodArgumentNotValidException e) { + assertEquals("simpleBean", e.getBindingResult().getObjectName()); + assertEquals(1, e.getBindingResult().getErrorCount()); + assertNotNull(e.getBindingResult().getFieldError("name")); } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java index 1db389a17b..be15c5630e 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java @@ -26,6 +26,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.TestBean; import org.springframework.beans.TypeMismatchException; +import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; @@ -36,9 +37,8 @@ import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.method.annotation.support.MethodArgumentNotValidException; import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.mvc.method.annotation.support.RequestBodyNotValidException; -import org.springframework.web.servlet.mvc.method.annotation.support.RequestPartNotValidException; import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; /** @author Arjen Poutsma */ @@ -136,24 +136,18 @@ public class DefaultHandlerExceptionResolverTests { } @Test - public void handleRequestBodyNotValid() { + public void handleMethodArgumentNotValid() throws Exception { BeanPropertyBindingResult errors = new BeanPropertyBindingResult(new TestBean(), "testBean"); errors.rejectValue("name", "invalid"); - RequestBodyNotValidException ex = new RequestBodyNotValidException(errors); + MethodParameter parameter = new MethodParameter(this.getClass().getMethod("handle", String.class), 0); + MethodArgumentNotValidException ex = new MethodArgumentNotValidException(parameter, errors); ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex); assertNotNull("No ModelAndView returned", mav); assertTrue("No Empty ModelAndView returned", mav.isEmpty()); assertEquals("Invalid status code", 400, response.getStatus()); } - @Test - public void handleRequestPartNotValid() { - BeanPropertyBindingResult errors = new BeanPropertyBindingResult(new TestBean(), "testBean"); - errors.rejectValue("name", "invalid"); - RequestPartNotValidException ex = new RequestPartNotValidException(errors); - ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex); - assertNotNull("No ModelAndView returned", mav); - assertTrue("No Empty ModelAndView returned", mav.isEmpty()); - assertEquals("Invalid status code", 400, response.getStatus()); + public void handle(String arg) { } + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/annotation/support/MethodArgumentNotValidException.java b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/support/MethodArgumentNotValidException.java new file mode 100644 index 0000000000..1f509aa016 --- /dev/null +++ b/org.springframework.web/src/main/java/org/springframework/web/method/annotation/support/MethodArgumentNotValidException.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2011 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.method.annotation.support; + +import org.springframework.core.MethodParameter; +import org.springframework.validation.BindingResult; +import org.springframework.validation.ObjectError; + +/** + * Thrown when validation on an argument annotated with {@code @Valid} fails. + * + * @author Rossen Stoyanchev + * @since 3.1 + */ +@SuppressWarnings("serial") +public class MethodArgumentNotValidException extends Exception { + + private final MethodParameter parameter; + + private final BindingResult bindingResult; + + /** + * Constructor for {@link MethodArgumentNotValidException}. + * @param parameter the parameter that failed validation + * @param bindingResult the results of the validation + */ + public MethodArgumentNotValidException(MethodParameter parameter, BindingResult bindingResult) { + this.parameter = parameter; + this.bindingResult = bindingResult; + } + + /** + * Return the method parameter that failed validation. + */ + public MethodParameter getParameter() { + return parameter; + } + + /** + * Return the results of the failed validation. + */ + public BindingResult getBindingResult() { + return bindingResult; + } + + @Override + public String getMessage() { + StringBuilder sb = new StringBuilder("Validation failed for argument at index [") + .append(parameter.getParameterIndex()).append("] in method: ") + .append(parameter.getMethod().toGenericString()) + .append(", with ").append(bindingResult.getErrorCount()).append(" error(s): "); + for (ObjectError error : bindingResult.getAllErrors()) { + sb.append("[").append(error).append("] "); + } + return sb.toString(); + } + +}