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
This commit is contained in:
parent
547b9638dc
commit
17089d607f
|
|
@ -55,6 +55,8 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
import org.springframework.web.servlet.View;
|
import org.springframework.web.servlet.View;
|
||||||
import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver;
|
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
|
* An {@link AbstractHandlerMethodExceptionResolver} that resolves exceptions
|
||||||
|
|
@ -303,6 +305,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
||||||
// Type-based argument resolution
|
// Type-based argument resolution
|
||||||
resolvers.add(new ServletRequestMethodArgumentResolver());
|
resolvers.add(new ServletRequestMethodArgumentResolver());
|
||||||
resolvers.add(new ServletResponseMethodArgumentResolver());
|
resolvers.add(new ServletResponseMethodArgumentResolver());
|
||||||
|
resolvers.add(new RedirectAttributesMethodArgumentResolver());
|
||||||
resolvers.add(new ModelMethodProcessor());
|
resolvers.add(new ModelMethodProcessor());
|
||||||
|
|
||||||
// Custom arguments
|
// Custom arguments
|
||||||
|
|
@ -401,6 +404,11 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
||||||
if (!mavContainer.isViewReference()) {
|
if (!mavContainer.isViewReference()) {
|
||||||
mav.setView((View) mavContainer.getView());
|
mav.setView((View) mavContainer.getView());
|
||||||
}
|
}
|
||||||
|
if (model instanceof RedirectAttributes) {
|
||||||
|
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
|
||||||
|
request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||||
|
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
|
||||||
|
}
|
||||||
return mav;
|
return mav;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,14 @@ public class RedirectAttributesMethodArgumentResolver implements HandlerMethodAr
|
||||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||||
|
|
||||||
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
|
ModelMap redirectAttributes;
|
||||||
ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder);
|
if(binderFactory != null) {
|
||||||
|
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
|
||||||
|
redirectAttributes = new RedirectAttributesModelMap(dataBinder);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
redirectAttributes = new RedirectAttributesModelMap();
|
||||||
|
}
|
||||||
mavContainer.setRedirectModel(redirectAttributes);
|
mavContainer.setRedirectModel(redirectAttributes);
|
||||||
return redirectAttributes;
|
return redirectAttributes;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,10 @@ import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.method.annotation.ModelMethodProcessor;
|
import org.springframework.web.method.annotation.ModelMethodProcessor;
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
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.ModelAndView;
|
||||||
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
import org.springframework.web.util.NestedServletException;
|
import org.springframework.web.util.NestedServletException;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
@ -53,6 +56,7 @@ import static org.junit.Assert.*;
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Kazuki Shimizu
|
* @author Kazuki Shimizu
|
||||||
|
* @author Brian Clozel
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
|
@ -82,6 +86,7 @@ public class ExceptionHandlerExceptionResolverTests {
|
||||||
this.resolver = new ExceptionHandlerExceptionResolver();
|
this.resolver = new ExceptionHandlerExceptionResolver();
|
||||||
this.resolver.setWarnLogCategory(this.resolver.getClass().getName());
|
this.resolver.setWarnLogCategory(this.resolver.getClass().getName());
|
||||||
this.request = new MockHttpServletRequest("GET", "/");
|
this.request = new MockHttpServletRequest("GET", "/");
|
||||||
|
this.request.setAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
|
||||||
this.response = new MockHttpServletResponse();
|
this.response = new MockHttpServletResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,6 +197,20 @@ public class ExceptionHandlerExceptionResolverTests {
|
||||||
assertEquals("IllegalArgumentException", mav.getModelMap().get("exceptionClassName"));
|
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
|
@Test
|
||||||
public void resolveExceptionGlobalHandler() throws Exception {
|
public void resolveExceptionGlobalHandler() throws Exception {
|
||||||
AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyConfig.class);
|
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
|
@RestControllerAdvice
|
||||||
@Order(1)
|
@Order(1)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue