diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index 2cf5406c34d..1db4cd6672b 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -40,6 +40,7 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.xml.SourceHttpMessageConverter; import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter; +import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.util.ReflectionUtils.MethodFilter; import org.springframework.validation.DataBinder; @@ -92,6 +93,7 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequ import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver; import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler; import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap; import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.util.WebUtils; @@ -145,6 +147,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore(); + private boolean alwaysUseRedirectAttributes; + private final Map, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap, SessionAttributesHandler>(); @@ -329,6 +333,22 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i this.parameterNameDiscoverer = parameterNameDiscoverer; } + /** + * By default a controller uses {@link Model} to select attributes for + * rendering and for redirecting. However, a controller can also use + * {@link RedirectAttributes} to select attributes before a redirect. + *

When this flag is set to {@code true}, {@link RedirectAttributes} + * becomes the only way to select attributes for a redirect. + * In other words, for a redirect a controller must use + * {@link RedirectAttributes} or no attributes will be used. + *

The default value is {@code false}, meaning the {@link Model} is + * used unless {@link RedirectAttributes} is used. + * @see RedirectAttributes + */ + public void setAlwaysUseRedirectAttributes(boolean alwaysUseRedirectAttributes) { + this.alwaysUseRedirectAttributes = alwaysUseRedirectAttributes; + } + public void setBeanFactory(BeanFactory beanFactory) { if (beanFactory instanceof ConfigurableBeanFactory) { this.beanFactory = (ConfigurableBeanFactory) beanFactory; @@ -510,13 +530,19 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); - - ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod); - ModelFactory modelFactory = getModelFactory(handlerMethod); + + WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); + ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory); + ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, requestMappingMethod); + + if (this.alwaysUseRedirectAttributes) { + DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null); + mavContainer.setRedirectModel(new RedirectAttributesModelMap(dataBinder)); + } SessionStatus sessionStatus = new SimpleSessionStatus(); @@ -536,23 +562,23 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } - return mav; + return mav; } } - private ServletInvocableHandlerMethod createRequestMappingMethod(HandlerMethod handlerMethod) { - ServletInvocableHandlerMethod requestMappingMethod = - new ServletInvocableHandlerMethod(handlerMethod.getBean(), handlerMethod.getMethod()); - requestMappingMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); - requestMappingMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); - requestMappingMethod.setDataBinderFactory(getDataBinderFactory(handlerMethod)); - requestMappingMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); - return requestMappingMethod; + private ServletInvocableHandlerMethod createRequestMappingMethod(HandlerMethod handlerMethod, + WebDataBinderFactory binderFactory) { + ServletInvocableHandlerMethod requestMethod; + requestMethod = new ServletInvocableHandlerMethod(handlerMethod.getBean(), handlerMethod.getMethod()); + requestMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); + requestMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); + requestMethod.setDataBinderFactory(binderFactory); + requestMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); + return requestMethod; } - private ModelFactory getModelFactory(HandlerMethod handlerMethod) { + private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) { SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod); - WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); Class handlerType = handlerMethod.getBeanType(); ModelFactory modelFactory = this.modelFactoryCache.get(handlerType); if (modelFactory == null) { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RedirectAttributesMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RedirectAttributesMethodArgumentResolver.java index dc878feacad..16c70320bed 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RedirectAttributesMethodArgumentResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RedirectAttributesMethodArgumentResolver.java @@ -49,10 +49,16 @@ public class RedirectAttributesMethodArgumentResolver implements HandlerMethodAr ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { - DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null); - ModelMap attributes = new RedirectAttributesModelMap(dataBinder); - mavContainer.setRedirectModel(attributes); - return attributes; + + if (mavContainer.getRedirectModel() != null) { + return mavContainer.getRedirectModel(); + } + else { + DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null); + ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder); + mavContainer.setRedirectModel(redirectAttributes); + return redirectAttributes; + } } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/ViewMethodReturnValueHandler.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/ViewMethodReturnValueHandler.java index 280c9608b4f..52604afad9f 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/ViewMethodReturnValueHandler.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/ViewMethodReturnValueHandler.java @@ -61,14 +61,14 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan String viewName = (String) returnValue; mavContainer.setViewName(viewName); if (isRedirectViewName(viewName)) { - mavContainer.setRedirectModelEnabled(); + mavContainer.setUseRedirectModel(); } } else if (returnValue instanceof View){ View view = (View) returnValue; mavContainer.setView(view); if (isRedirectView(view)) { - mavContainer.setRedirectModelEnabled(); + mavContainer.setUseRedirectModel(); } } else { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributesModelMap.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributesModelMap.java index d98fd4a6ee4..7b28b729b6c 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributesModelMap.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributesModelMap.java @@ -66,13 +66,14 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr *

Formats the attribute value as a String before adding it. */ public RedirectAttributesModelMap addAttribute(String attributeName, Object attributeValue) { - if (attributeValue != null) { - super.addAttribute(attributeName, formatValue(attributeValue)); - } + super.addAttribute(attributeName, formatValue(attributeValue)); return this; } private String formatValue(Object value) { + if (value == null) { + return null; + } return (dataBinder != null) ? dataBinder.convertIfNecessary(value, String.class) : value.toString(); } @@ -126,6 +127,28 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr return this; } + /** + * {@inheritDoc} + *

The value is formatted as a String before being added. + */ + @Override + public Object put(String key, Object value) { + return super.put(key, formatValue(value)); + } + + /** + * {@inheritDoc} + *

Each value is formatted as a String before being added. + */ + @Override + public void putAll(Map map) { + if (map != null) { + for (String key : map.keySet()) { + put(key, formatValue(map.get(key))); + } + } + } + public RedirectAttributes addFlashAttribute(String attributeName, Object attributeValue) { this.flashAttributes.addAttribute(attributeName, attributeValue); return this; diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterTests.java index ccf6246c9ae..b3462ffd309 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterTests.java @@ -30,6 +30,7 @@ import org.springframework.beans.DirectFieldAccessor; import org.springframework.core.MethodParameter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; @@ -42,19 +43,21 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite; import org.springframework.web.method.support.InvocableHandlerMethod; import org.springframework.web.method.support.ModelAndViewContainer; +import org.springframework.web.servlet.FlashMap; +import org.springframework.web.servlet.FlashMapManager; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.method.annotation.support.RedirectAttributesMethodArgumentResolver; import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver; +import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler; /** - * Fine-grained {@link RequestMappingHandlerAdapter} unit tests. - * - *

For higher-level adapter tests see: - *

+ * Unit tests for {@link RequestMappingHandlerAdapter}. * * @author Rossen Stoyanchev + * + * @see ServletAnnotationControllerHandlerMethodTests + * @see HandlerMethodAnnotationDetectionTests + * @see RequestMappingHandlerAdapterIntegrationTests */ public class RequestMappingHandlerAdapterTests { @@ -68,35 +71,56 @@ public class RequestMappingHandlerAdapterTests { public void setup() throws Exception { this.handlerAdapter = new RequestMappingHandlerAdapter(); this.handlerAdapter.setApplicationContext(new GenericWebApplicationContext()); - this.request = new MockHttpServletRequest(); this.response = new MockHttpServletResponse(); } @Test public void cacheControlWithoutSessionAttributes() throws Exception { + SimpleHandler handler = new SimpleHandler(); handlerAdapter.afterPropertiesSet(); handlerAdapter.setCacheSeconds(100); - handlerAdapter.handle(request, response, handlerMethod(new SimpleHandler(), "handle")); + handlerAdapter.handle(request, response, handlerMethod(handler, "handle")); assertTrue(response.getHeader("Cache-Control").toString().contains("max-age")); } @Test public void cacheControlWithSessionAttributes() throws Exception { + SessionAttributeHandler handler = new SessionAttributeHandler(); handlerAdapter.afterPropertiesSet(); handlerAdapter.setCacheSeconds(100); - handlerAdapter.handle(request, response, handlerMethod(new SessionAttributeHandler(), "handle")); + handlerAdapter.handle(request, response, handlerMethod(handler, "handle")); assertEquals("no-cache", response.getHeader("Cache-Control")); } + @Test + public void setAlwaysUseRedirectAttributes() throws Exception { + HandlerMethodArgumentResolver redirectAttributesResolver = new RedirectAttributesMethodArgumentResolver(); + HandlerMethodArgumentResolver modelResolver = new ModelMethodProcessor(); + HandlerMethodReturnValueHandler viewHandler = new ViewMethodReturnValueHandler(); + + handlerAdapter.setArgumentResolvers(Arrays.asList(redirectAttributesResolver, modelResolver)); + handlerAdapter.setReturnValueHandlers(Arrays.asList(viewHandler)); + handlerAdapter.setAlwaysUseRedirectAttributes(true); + handlerAdapter.afterPropertiesSet(); + + request.setAttribute(FlashMapManager.OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); + + HandlerMethod handlerMethod = handlerMethod(new RedirectAttributeHandler(), "handle", Model.class); + ModelAndView mav = handlerAdapter.handle(request, response, handlerMethod); + + assertTrue("No redirect attributes added, model should be empty", mav.getModel().isEmpty()); + } + @Test @SuppressWarnings("unchecked") public void setArgumentResolvers() { - List expected = new ArrayList(); - expected.add(new ServletRequestMethodArgumentResolver()); - handlerAdapter.setArgumentResolvers(expected); + List argumentResolvers = new ArrayList(); + argumentResolvers.add(new ServletRequestMethodArgumentResolver()); + + handlerAdapter.setArgumentResolvers(argumentResolvers); handlerAdapter.afterPropertiesSet(); HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite) @@ -105,15 +129,16 @@ public class RequestMappingHandlerAdapterTests { List actual = (List) new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers"); - assertEquals(expected, actual); + assertEquals(argumentResolvers, actual); } @Test @SuppressWarnings("unchecked") public void setInitBinderArgumentResolvers() { - List expected = new ArrayList(); - expected.add(new ServletRequestMethodArgumentResolver()); - handlerAdapter.setInitBinderArgumentResolvers(expected); + List argumentResolvers = new ArrayList(); + argumentResolvers.add(new ServletRequestMethodArgumentResolver()); + + handlerAdapter.setInitBinderArgumentResolvers(argumentResolvers); handlerAdapter.afterPropertiesSet(); HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite) @@ -122,15 +147,16 @@ public class RequestMappingHandlerAdapterTests { List actual = (List) new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers"); - assertEquals(expected, actual); + assertEquals(argumentResolvers, actual); } @Test @SuppressWarnings("unchecked") public void setReturnValueHandlers() { - List expected = new ArrayList(); - expected.add(new ModelMethodProcessor()); - handlerAdapter.setReturnValueHandlers(expected); + HandlerMethodReturnValueHandler handler = new ModelMethodProcessor(); + List handlers = Arrays.asList(handler); + + handlerAdapter.setReturnValueHandlers(handlers); handlerAdapter.afterPropertiesSet(); HandlerMethodReturnValueHandlerComposite composite = (HandlerMethodReturnValueHandlerComposite) @@ -139,14 +165,14 @@ public class RequestMappingHandlerAdapterTests { List actual = (List) new DirectFieldAccessor(composite).getPropertyValue("returnValueHandlers"); - assertEquals(expected, actual); + assertEquals(handlers, actual); } @Test @SuppressWarnings("unchecked") public void setCustomArgumentResolvers() { - TestHanderMethodArgumentResolver resolver = new TestHanderMethodArgumentResolver(); - handlerAdapter.setCustomArgumentResolvers(Arrays.asList(resolver)); + HandlerMethodArgumentResolver resolver = new TestHanderMethodArgumentResolver(); + handlerAdapter.setCustomArgumentResolvers(Arrays.asList(resolver)); handlerAdapter.afterPropertiesSet(); HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite) @@ -181,13 +207,14 @@ public class RequestMappingHandlerAdapterTests { assertTrue(actual.contains(handler)); } - + private HandlerMethod handlerMethod(Object handler, String methodName, Class... paramTypes) throws Exception { Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes); return new InvocableHandlerMethod(handler, method); } private final class TestHanderMethodArgumentResolver implements HandlerMethodArgumentResolver { + public boolean supportsParameter(MethodParameter parameter) { return false; } @@ -209,19 +236,23 @@ public class RequestMappingHandlerAdapterTests { } } - private static class SimpleHandler { - - @SuppressWarnings("unused") + static class SimpleHandler { public void handle() { } } @SessionAttributes("attr1") - private static class SessionAttributeHandler { - - @SuppressWarnings("unused") + static class SessionAttributeHandler { public void handle() { } } + static class RedirectAttributeHandler { + public String handle(Model model) { + model.addAttribute("someAttr", "someAttrValue"); + return "redirect:/path"; + } + } + + } \ No newline at end of file diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index 75ce089bc4e..d738461c7a4 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -1456,8 +1456,8 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl } @Test - public void flashAttribute() throws Exception { - initServletWithControllers(MessageController.class); + public void redirectAttribute() throws Exception { + initServletWithControllers(RedirectAttributesController.class); MockHttpServletRequest request = new MockHttpServletRequest("POST", "/messages"); HttpSession session = request.getSession(); @@ -2803,7 +2803,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl } @Controller - static class MessageController { + static class RedirectAttributesController { @InitBinder public void initBinder(WebDataBinder dataBinder) { @@ -2827,8 +2827,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl } } } - - + // Test cases deleted from the original SevletAnnotationControllerTests: // @Ignore("Controller interface => no method-level @RequestMapping annotation") diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/support/RedirectAttributesModelMapTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/support/RedirectAttributesModelMapTests.java index e67da0b9ab2..6662b3acb9f 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/support/RedirectAttributesModelMapTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/support/RedirectAttributesModelMapTests.java @@ -119,6 +119,24 @@ public class RedirectAttributesModelMapTests { assertEquals("33", this.redirectAttributes.get("age")); } + @Test + public void put() { + this.redirectAttributes.put("testBean", new TestBean("Fred")); + + assertEquals("Fred", this.redirectAttributes.get("testBean")); + } + + @Test + public void putAll() { + Map map = new HashMap(); + map.put("person", new TestBean("Fred")); + map.put("age", 33); + this.redirectAttributes.putAll(map); + + assertEquals("Fred", this.redirectAttributes.get("person")); + assertEquals("33", this.redirectAttributes.get("age")); + } + public static class TestBeanConverter implements Converter { public String convert(TestBean source) { diff --git a/org.springframework.web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java b/org.springframework.web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java index a3a3cefa8d5..86cd6d17b64 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java +++ b/org.springframework.web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java @@ -23,9 +23,19 @@ import org.springframework.ui.ModelMap; import org.springframework.validation.support.BindingAwareModelMap; /** - * Record model and view related decisions made by {@link HandlerMethodArgumentResolver}s - * and {@link HandlerMethodReturnValueHandler}s during the course of invocation of a - * request-handling method. + * Records model and view related decisions made by + * {@link HandlerMethodArgumentResolver}s and + * {@link HandlerMethodReturnValueHandler}s during the course of invocation of + * a controller method. + * + *

The {@link #setResolveView(boolean)} flag can be used to indicate that + * view resolution is not required (e.g. {@code @ResponseBody} method). + * + *

A default {@link Model} is created at instantiation and used thereafter. + * The {@link #setRedirectModel(ModelMap)} method can be used to provide a + * separate model to use potentially in case of a redirect. + * The {@link #setUseRedirectModel()} can be used to enable use of the + * redirect model if the controller decides to redirect. * * @author Rossen Stoyanchev * @since 3.1 @@ -40,7 +50,7 @@ public class ModelAndViewContainer { private ModelMap redirectModel; - private boolean redirectModelEnabled; + private boolean useRedirectModel = false; /** * Create a new instance. @@ -89,12 +99,15 @@ public class ModelAndViewContainer { } /** - * Whether view resolution is required or not. The default value is "true". - *

When set to "false" by a {@link HandlerMethodReturnValueHandler}, the response - * is considered complete and view resolution is not be performed. - *

When set to "false" by {@link HandlerMethodArgumentResolver}, the response is - * considered complete only in combination with the request mapping method - * returning {@code null} or void. + * Whether view resolution is required or not. + *

A {@link HandlerMethodReturnValueHandler} may use this flag to + * indicate the response has been fully handled and view resolution + * is not required (e.g. {@code @ResponseBody}). + *

A {@link HandlerMethodArgumentResolver} may also use this flag + * to indicate the presence of an argument (e.g. + * {@code ServletResponse} or {@code OutputStream}) that may lead to + * a complete response depending on the method return value. + *

The default value is {@code true}. */ public void setResolveView(boolean resolveView) { this.resolveView = resolveView; @@ -108,32 +121,42 @@ public class ModelAndViewContainer { } /** - * Return the model to use, never {@code null}. + * Return the default model created at instantiation or the one provided + * via {@link #setRedirectModel(ModelMap)} as long as it has been enabled + * via {@link #setUseRedirectModel()}. */ public ModelMap getModel() { - if (this.redirectModelEnabled && (this.redirectModel != null)) { + if ((this.redirectModel != null) && this.useRedirectModel) { return this.redirectModel; } else { return this.model; } } - + /** - * Provide an alternative model that may be prepared for a specific redirect - * case. To enable use of this model, {@link #setRedirectModelEnabled()} - * must also be called. + * Provide a model instance to use in case the controller redirects. + * Note that {@link #setUseRedirectModel()} must also be called in order + * to enable use of the redirect model. */ public void setRedirectModel(ModelMap redirectModel) { this.redirectModel = redirectModel; } /** - * Signals that a redirect model provided via {@link #setRedirectModel} - * may be used if it was provided. + * Return the redirect model provided via + * {@link #setRedirectModel(ModelMap)} or {@code null} if not provided. + */ + public ModelMap getRedirectModel() { + return this.redirectModel; + } + + /** + * Indicate that the redirect model provided via + * {@link #setRedirectModel(ModelMap)} should be used. */ - public void setRedirectModelEnabled() { - this.redirectModelEnabled = true; + public void setUseRedirectModel() { + this.useRedirectModel = true; } /** @@ -180,5 +203,26 @@ public class ModelAndViewContainer { public boolean containsAttribute(String name) { return getModel().containsAttribute(name); } + + /** + * Return diagnostic information. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder("ModelAndViewContainer: "); + if (isResolveView()) { + if (isViewReference()) { + sb.append("reference to view with name '").append(this.view).append("'"); + } + else { + sb.append("View is [").append(this.view).append(']'); + } + sb.append("; model is ").append(getModel()); + } + else { + sb.append("View resolution not required"); + } + return sb.toString(); + } }