From ea05e0b1add86317c13997678aa01bfb73476487 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 23 Dec 2014 14:15:41 -0500 Subject: [PATCH] Improve @SessionAttributes support during redirect Before this change attributes listed with @SessionAttributes would not be saved in the session when there was a redirect and the controller method declared a parameter of type RedirectAttributes. This change ensures it's the "default" model that is always the one checked for @SessionAttributes under all circumstances since RedirectAttributes is really only meant to provide String values to insert into or append to the the redirect URL. Issue: SPR-12542 --- .../web/method/annotation/ModelFactory.java | 7 ++--- .../method/support/ModelAndViewContainer.java | 13 ++++++++++ .../method/annotation/ModelFactoryTests.java | 26 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java index e1774e65a7c..9ee2bbdea3f 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java @@ -224,14 +224,15 @@ public final class ModelFactory { * @throws Exception if creating BindingResult attributes fails */ public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception { + ModelMap defaultModel = mavContainer.getDefaultModel(); if (mavContainer.getSessionStatus().isComplete()){ this.sessionAttributesHandler.cleanupAttributes(request); } else { - this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel()); + this.sessionAttributesHandler.storeAttributes(request, defaultModel); } - if (!mavContainer.isRequestHandled()) { - updateBindingResult(request, mavContainer.getModel()); + if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) { + updateBindingResult(request, defaultModel); } } diff --git a/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java b/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java index 481fcbe4ff1..d485607516c 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java @@ -137,6 +137,19 @@ public class ModelAndViewContainer { return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect)); } + /** + * Return the "default" model created at instantiation. + *

In general it is recommended to use {@link #getModel()} instead which + * returns either the "default" model (template rendering) or the "redirect" + * model (redirect URL preparation). Use of this method may be needed for + * advanced cases when access to the "default" model is needed regardless, + * e.g. to save model attributes specified via {@code @SessionAttributes}. + * @return the default model, never {@code null} + */ + public ModelMap getDefaultModel() { + return this.defaultModel; + } + /** * Provide a separate model instance to use in a redirect scenario. * The provided additional model however is not used used unless diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryTests.java index 45f8cbbf2b3..e0a3c69f9c7 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryTests.java @@ -215,6 +215,32 @@ public class ModelFactoryTests { assertNull(this.sessionAttributeStore.retrieveAttribute(this.webRequest, attributeName)); } + // SPR-12542 + + @Test + public void updateModelWhenRedirecting() throws Exception { + String attributeName = "sessionAttr"; + String attribute = "value"; + ModelAndViewContainer container = new ModelAndViewContainer(); + container.addAttribute(attributeName, attribute); + + String queryParam = "123"; + String queryParamName = "q"; + container.setRedirectModel(new ModelMap(queryParamName, queryParam)); + container.setRedirectModelScenario(true); + + WebDataBinder dataBinder = new WebDataBinder(attribute, attributeName); + WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class); + given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder); + + ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.sessionAttrsHandler); + modelFactory.updateModel(this.webRequest, container); + + assertEquals(queryParam, container.getModel().get(queryParamName)); + assertEquals(1, container.getModel().size()); + assertEquals(attribute, this.sessionAttributeStore.retrieveAttribute(this.webRequest, attributeName)); + } + private String bindingResultKey(String key) { return BindingResult.MODEL_KEY_PREFIX + key;