From 9d2ee7061c93453d9cac411aee6df03e34b12a88 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 2 Jun 2011 18:27:50 +0000 Subject: [PATCH] SPR-6709 Handle RequestBodyNotValidException and update reference docs --- .../support/RequestBodyNotValidException.java | 11 +++++++++ .../DefaultHandlerExceptionResolver.java | 23 ++++++++++++++++++- ...questResponseBodyMethodProcessorTests.java | 3 +-- spring-framework-reference/src/mvc.xml | 11 ++++++++- 4 files changed, 44 insertions(+), 4 deletions(-) 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 index ab8482bdc15..3ebdebd2bb2 100644 --- 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 @@ -17,6 +17,7 @@ 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; /** @@ -44,5 +45,15 @@ public class RequestBodyNotValidException extends RuntimeException { 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/support/DefaultHandlerExceptionResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java index c33907d0351..9589e65438c 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 @@ -16,13 +16,14 @@ package org.springframework.web.servlet.mvc.support; +import java.io.IOException; import java.util.List; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.ConversionNotSupportedException; import org.springframework.beans.TypeMismatchException; import org.springframework.core.Ordered; @@ -37,6 +38,7 @@ import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MissingServletRequestParameterException; 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.multiaction.NoSuchRequestHandlingMethodException; /** @@ -116,6 +118,9 @@ 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); + } } catch (Exception handlerException) { logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException); @@ -314,4 +319,20 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes return new ModelAndView(); } + /** + * Handle the case where the object created from the body of a request has failed validation. The default + * implementation sends an HTTP 500 error along with a message containing the errors. + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or null if none chosen + * at the time of the exception (for example, if multipart resolution failed) + * @return a ModelAndView to render, or null if handled directly + * @throws Exception an Exception that should be thrown as result of the servlet request + */ + protected ModelAndView handleRequestBodyNotValidException(RequestBodyNotValidException ex, + HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); + return new ModelAndView(); + } + } 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 b7d69f3f8d1..9e0d66ab5ff 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 @@ -23,9 +23,9 @@ import static org.easymock.EasyMock.isA; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.reset; import static org.easymock.EasyMock.verify; -import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -49,7 +49,6 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.validation.DataBinder; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; diff --git a/spring-framework-reference/src/mvc.xml b/spring-framework-reference/src/mvc.xml index 6ac86fdd783..e893fa569f0 100644 --- a/spring-framework-reference/src/mvc.xml +++ b/spring-framework-reference/src/mvc.xml @@ -1369,9 +1369,18 @@ public void handle(@RequestBody String body, Writer writer) throws IOException { <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/> + An @RequestBody method parameter can be annotated with + @Valid, in which case it will validated using the configured + Validator instance. When using the MVC namespace a JSR-303 + validator is configured automatically assuming a JSR-303 implementation is available + on the classpath. If validation fails a RequestBodyNotValidException + is raised. The exception is handled by the DefaultHandlerExceptionResolver + and results in a 500 error send back to the client along with + a message containing the validation errors. + Also see for information on - configuring message converters through the MVC namespace. + configuring message converters and a validator through the MVC namespace.