From 1a6dc86fed721bdcf0aa01be8a5d587ddab9b5a6 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 9 Dec 2016 23:41:22 +0100 Subject: [PATCH] Allow RedirectAttributes on ExceptionHandlers Prior to this commit, `@ExceptionHandler` methods could not be injected with `RedirectAttributes` arguments. This would make it impossible to handle an error by redirecting to another view and add flashmap attributes, to be included in the model when the next view is called. Here is an example: ``` @ExceptionHandler(MyException.class) public String handleException(MyException ex, RedirectAttributes redirectAttributes) { redirectAttributes.addFlashAttribute("errorMessage", "This is an error message"); return "redirect:/"; } ``` This commit adds a new `RedirectAttributesMethodArgumentResolver` instance in the list of pre-configured `HandlerMethodArgumentResolver` in `ExceptionHandlerExceptionResolver`. Issue: SPR-14651 Cherry-picked from: 17089d6 --- .../ExceptionHandlerExceptionResolver.java | 8 +++++ ...irectAttributesMethodArgumentResolver.java | 10 ++++-- ...xceptionHandlerExceptionResolverTests.java | 31 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java index b175b62df76..cf1bda53bac 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java @@ -56,6 +56,8 @@ import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.View; import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.support.RequestContextUtils; /** * An {@link AbstractHandlerMethodExceptionResolver} that resolves exceptions @@ -304,6 +306,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); + resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); // Custom arguments @@ -402,6 +405,11 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } + if (model instanceof RedirectAttributes) { + Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); + request = webRequest.getNativeRequest(HttpServletRequest.class); + RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); + } return mav; } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RedirectAttributesMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RedirectAttributesMethodArgumentResolver.java index a4126f5e397..af19698ff04 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RedirectAttributesMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RedirectAttributesMethodArgumentResolver.java @@ -53,8 +53,14 @@ public class RedirectAttributesMethodArgumentResolver implements HandlerMethodAr public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { - DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null); - ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder); + ModelMap redirectAttributes; + if(binderFactory != null) { + DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null); + redirectAttributes = new RedirectAttributesModelMap(dataBinder); + } + else { + redirectAttributes = new RedirectAttributesModelMap(); + } mavContainer.setRedirectModel(redirectAttributes); return redirectAttributes; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java index f633f73b016..34355dac162 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java @@ -42,7 +42,10 @@ import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.annotation.ModelMethodProcessor; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.util.NestedServletException; import static org.junit.Assert.*; @@ -53,6 +56,7 @@ import static org.junit.Assert.*; * @author Rossen Stoyanchev * @author Arjen Poutsma * @author Kazuki Shimizu + * @author Brian Clozel * @since 3.1 */ @SuppressWarnings("unused") @@ -82,6 +86,7 @@ public class ExceptionHandlerExceptionResolverTests { this.resolver = new ExceptionHandlerExceptionResolver(); this.resolver.setWarnLogCategory(this.resolver.getClass().getName()); this.request = new MockHttpServletRequest("GET", "/"); + this.request.setAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); this.response = new MockHttpServletResponse(); } @@ -192,6 +197,20 @@ public class ExceptionHandlerExceptionResolverTests { assertEquals("IllegalArgumentException", mav.getModelMap().get("exceptionClassName")); } + @Test // SPR-14651 + public void resolveRedirectAttributesAtArgument() throws Exception { + IllegalArgumentException ex = new IllegalArgumentException(); + HandlerMethod handlerMethod = new HandlerMethod(new RedirectAttributesController(), "handle"); + this.resolver.afterPropertiesSet(); + ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex); + + assertNotNull(mav); + assertEquals("redirect:/", mav.getViewName()); + FlashMap flashMap = (FlashMap) this.request.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE); + assertNotNull("output FlashMap should exist", flashMap); + assertEquals("IllegalArgumentException", flashMap.get("exceptionClassName")); + } + @Test public void resolveExceptionGlobalHandler() throws Exception { AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyConfig.class); @@ -364,6 +383,18 @@ public class ExceptionHandlerExceptionResolverTests { } } + @Controller + static class RedirectAttributesController { + + public void handle() {} + + @ExceptionHandler + public String handleException(Exception ex, RedirectAttributes redirectAttributes) { + redirectAttributes.addFlashAttribute("exceptionClassName", ClassUtils.getShortName(ex.getClass())); + return "redirect:/"; + } + } + @RestControllerAdvice @Order(1)