From a1b7a314c1645018c5c2088775fd1a9bd282ee14 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 10 Jul 2012 17:27:51 -0400 Subject: [PATCH] Add BindException to DefaultHandlerExceptionResolver Previously DefaultHandlerExceptionResolver did not handle BindException but after this change it does. A BindException is raised when an @ModelAttribute annotated argument is not followed by a BindingResult argument. Hence this is unlikely to affect browser rendering. For programmatic clients however this change ensures an unhandled BindException is at least turned into a 400 error. Issue: SPR-9310 --- .../DefaultHandlerExceptionResolver.java | 36 ++++++++++++++++--- .../DefaultHandlerExceptionResolverTests.java | 12 ++++++- src/dist/changelog.txt | 1 + 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java index acd0cd110258..b597f9e49d0d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -32,12 +32,15 @@ import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; @@ -65,6 +68,9 @@ * @see #handleHttpMessageNotReadable * @see #handleHttpMessageNotWritable * @see #handleMethodArgumentNotValidException + * @see #handleMissingServletRequestParameter + * @see #handleMissingServletRequestPart + * @see #handleBindException */ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver { @@ -136,6 +142,9 @@ else if (ex instanceof MethodArgumentNotValidException) { else if (ex instanceof MissingServletRequestPartException) { return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request, response, handler); } + else if (ex instanceof BindException) { + return handleBindException((BindException) ex, request, response, handler); + } } catch (Exception handlerException) { logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException); @@ -346,8 +355,8 @@ protected ModelAndView handleHttpMessageNotWritable(HttpMessageNotWritableExcept } /** - * Handle the case where an argument annotated with {@code @Valid} such as - * an {@link RequestBody} or {@link RequestPart} argument fails validation. + * 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 @@ -362,8 +371,8 @@ protected ModelAndView handleMethodArgumentNotValidException(MethodArgumentNotVa } /** - * Handle the case where an @{@link RequestPart}, a {@link MultipartFile}, - * or a {@code javax.servlet.http.Part} argument is required but missing. + * Handle the case where an {@linkplain RequestPart @RequestPart}, a {@link MultipartFile}, + * or a {@code javax.servlet.http.Part} argument is required but is missing. * An HTTP 400 error is sent back to the client. * @param request current HTTP request * @param response current HTTP response @@ -377,4 +386,21 @@ protected ModelAndView handleMissingServletRequestPartException(MissingServletRe return new ModelAndView(); } + /** + * Handle the case where an {@linkplain ModelAttribute @ModelAttribute} method + * argument has binding or validation errors and is not followed by another + * method argument of type {@link BindingResult}. + * By default 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 handleBindException(BindException ex, HttpServletRequest request, + HttpServletResponse response, Object handler) throws IOException { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return new ModelAndView(); + } + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java index db9b60af79ba..0ef1163958c2 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2012 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. @@ -33,6 +33,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.validation.BeanPropertyBindingResult; +import org.springframework.validation.BindException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -157,6 +158,15 @@ public void handleMissingServletRequestPartException() throws Exception { assertEquals("Invalid status code", 400, response.getStatus()); } + @Test + public void handleBindException() throws Exception { + BindException ex = new BindException(new Object(), "name"); + 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/src/dist/changelog.txt b/src/dist/changelog.txt index cc0bcca8c784..09a15724e640 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -21,6 +21,7 @@ Changes in version 3.2 M2 (2012-08-xx) * add defaultCharset property to StringHttpMessageConverter * add @ExceptionResolver annotation to detect classes with @ExceptionHandler methods * move RSS/Atom message converter registration ahead of jackson/jaxb2 +* handle BindException in DefaultHandlerExceptionResolver Changes in version 3.2 M1 (2012-05-28)