Skip to content

Commit 1a6dc86

Browse files
committed
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
1 parent 35b0c8b commit 1a6dc86

File tree

3 files changed

+47
-2
lines changed

3 files changed

+47
-2
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import org.springframework.web.servlet.ModelAndView;
5757
import org.springframework.web.servlet.View;
5858
import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver;
59+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
60+
import org.springframework.web.servlet.support.RequestContextUtils;
5961

6062
/**
6163
* An {@link AbstractHandlerMethodExceptionResolver} that resolves exceptions
@@ -304,6 +306,7 @@ protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
304306
// Type-based argument resolution
305307
resolvers.add(new ServletRequestMethodArgumentResolver());
306308
resolvers.add(new ServletResponseMethodArgumentResolver());
309+
resolvers.add(new RedirectAttributesMethodArgumentResolver());
307310
resolvers.add(new ModelMethodProcessor());
308311

309312
// Custom arguments
@@ -402,6 +405,11 @@ protected ModelAndView doResolveHandlerMethodException(HttpServletRequest reques
402405
if (!mavContainer.isViewReference()) {
403406
mav.setView((View) mavContainer.getView());
404407
}
408+
if (model instanceof RedirectAttributes) {
409+
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
410+
request = webRequest.getNativeRequest(HttpServletRequest.class);
411+
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
412+
}
405413
return mav;
406414
}
407415
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,14 @@ public boolean supportsParameter(MethodParameter parameter) {
5353
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
5454
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
5555

56-
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
57-
ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder);
56+
ModelMap redirectAttributes;
57+
if(binderFactory != null) {
58+
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
59+
redirectAttributes = new RedirectAttributesModelMap(dataBinder);
60+
}
61+
else {
62+
redirectAttributes = new RedirectAttributesModelMap();
63+
}
5864
mavContainer.setRedirectModel(redirectAttributes);
5965
return redirectAttributes;
6066
}

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@
4242
import org.springframework.web.method.annotation.ModelMethodProcessor;
4343
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
4444
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
45+
import org.springframework.web.servlet.DispatcherServlet;
46+
import org.springframework.web.servlet.FlashMap;
4547
import org.springframework.web.servlet.ModelAndView;
48+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
4649
import org.springframework.web.util.NestedServletException;
4750

4851
import static org.junit.Assert.*;
@@ -53,6 +56,7 @@
5356
* @author Rossen Stoyanchev
5457
* @author Arjen Poutsma
5558
* @author Kazuki Shimizu
59+
* @author Brian Clozel
5660
* @since 3.1
5761
*/
5862
@SuppressWarnings("unused")
@@ -82,6 +86,7 @@ public void setup() throws Exception {
8286
this.resolver = new ExceptionHandlerExceptionResolver();
8387
this.resolver.setWarnLogCategory(this.resolver.getClass().getName());
8488
this.request = new MockHttpServletRequest("GET", "/");
89+
this.request.setAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
8590
this.response = new MockHttpServletResponse();
8691
}
8792

@@ -192,6 +197,20 @@ public void resolveExceptionModelAtArgument() throws Exception {
192197
assertEquals("IllegalArgumentException", mav.getModelMap().get("exceptionClassName"));
193198
}
194199

200+
@Test // SPR-14651
201+
public void resolveRedirectAttributesAtArgument() throws Exception {
202+
IllegalArgumentException ex = new IllegalArgumentException();
203+
HandlerMethod handlerMethod = new HandlerMethod(new RedirectAttributesController(), "handle");
204+
this.resolver.afterPropertiesSet();
205+
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
206+
207+
assertNotNull(mav);
208+
assertEquals("redirect:/", mav.getViewName());
209+
FlashMap flashMap = (FlashMap) this.request.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE);
210+
assertNotNull("output FlashMap should exist", flashMap);
211+
assertEquals("IllegalArgumentException", flashMap.get("exceptionClassName"));
212+
}
213+
195214
@Test
196215
public void resolveExceptionGlobalHandler() throws Exception {
197216
AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyConfig.class);
@@ -364,6 +383,18 @@ public void handleException(Exception ex, Model model) {
364383
}
365384
}
366385

386+
@Controller
387+
static class RedirectAttributesController {
388+
389+
public void handle() {}
390+
391+
@ExceptionHandler
392+
public String handleException(Exception ex, RedirectAttributes redirectAttributes) {
393+
redirectAttributes.addFlashAttribute("exceptionClassName", ClassUtils.getShortName(ex.getClass()));
394+
return "redirect:/";
395+
}
396+
}
397+
367398

368399
@RestControllerAdvice
369400
@Order(1)

0 commit comments

Comments
 (0)