diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java index 13c47eab1e..dbf71b14cf 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java @@ -60,7 +60,6 @@ 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.ServletWebArgumentResolverAdapter; import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler; -import org.springframework.web.servlet.mvc.method.support.ResponseContext; /** * An {@link AbstractHandlerMethodExceptionResolver} that supports using {@link ExceptionHandler}-annotated methods @@ -214,10 +213,10 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, - Exception ex) { + Exception exception) { if (handlerMethod != null) { ExceptionMethodMapping mapping = getExceptionMethodMapping(handlerMethod); - Method method = mapping.getMethod(ex); + Method method = mapping.getMethod(exception); if (method != null) { Object handler = handlerMethod.getBean(); @@ -232,9 +231,8 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce } ModelAndViewContainer mavContainer = new ModelAndViewContainer(); - ResponseContext responseContext = new ResponseContext(webRequest, mavContainer); - exceptionHandler.invokeAndHandle(webRequest, mavContainer, ex, responseContext); + exceptionHandler.invokeAndHandle(webRequest, mavContainer, exception); if (!mavContainer.isResolveView()) { return new ModelAndView(); 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 ddc1594467..6bc2152b4f 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.ModelMap; import org.springframework.util.ReflectionUtils.MethodFilter; import org.springframework.validation.DataBinder; import org.springframework.web.bind.WebDataBinder; @@ -84,6 +85,7 @@ import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMeth import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor; import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler; import org.springframework.web.servlet.mvc.method.annotation.support.PathVariableMethodArgumentResolver; +import org.springframework.web.servlet.mvc.method.annotation.support.RedirectModelMethodArgumentResolver; import org.springframework.web.servlet.mvc.method.annotation.support.RequestPartMethodArgumentResolver; import org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor; import org.springframework.web.servlet.mvc.method.annotation.support.ServletCookieValueMethodArgumentResolver; @@ -91,7 +93,8 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletMode import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver; 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.method.support.ResponseContext; +import org.springframework.web.servlet.mvc.support.RedirectModel; +import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.util.WebUtils; /** @@ -366,6 +369,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i argumentResolvers.addResolver(new ServletRequestMethodArgumentResolver()); argumentResolvers.addResolver(new ServletResponseMethodArgumentResolver()); argumentResolvers.addResolver(new HttpEntityMethodProcessor(messageConverters)); + argumentResolvers.addResolver(new RedirectModelMethodArgumentResolver()); argumentResolvers.addResolver(new ModelMethodProcessor()); argumentResolvers.addResolver(new ErrorsMethodArgumentResolver()); @@ -512,27 +516,32 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod); - FlashMap flashMap = (FlashMap) request.getAttribute(FlashMapManager.PREVIOUS_FLASH_MAP_ATTRIBUTE); + FlashMap previousFlashMap = (FlashMap) request.getAttribute(FlashMapManager.PREVIOUS_FLASH_MAP_ATTRIBUTE); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); - mavContainer.addAllAttributes(flashMap); + mavContainer.addAllAttributes(previousFlashMap); modelFactory.initModel(webRequest, mavContainer, requestMappingMethod); SessionStatus sessionStatus = new SimpleSessionStatus(); - ResponseContext responseContext = new ResponseContext(webRequest, mavContainer); - requestMappingMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus, responseContext); + requestMappingMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus); modelFactory.updateModel(webRequest, mavContainer, sessionStatus); if (!mavContainer.isResolveView()) { return null; } else { - ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel()); + ModelMap model = mavContainer.getModel(); + ModelAndView mav = new ModelAndView().addAllObjects(model); mav.setViewName(mavContainer.getViewName()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } + if (model instanceof RedirectModel) { + RedirectModel redirectModel = (RedirectModel) model; + FlashMap currentFlashMap = RequestContextUtils.getFlashMap(request); + currentFlashMap.putAll(redirectModel.getFlashAttributes()); + } return mav; } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RedirectModelMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RedirectModelMethodArgumentResolver.java new file mode 100644 index 0000000000..5c353b794f --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RedirectModelMethodArgumentResolver.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.mvc.method.annotation.support; + +import org.springframework.core.MethodParameter; +import org.springframework.ui.Model; +import org.springframework.ui.ModelMap; +import org.springframework.validation.DataBinder; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.annotation.support.ModelMethodProcessor; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import org.springframework.web.servlet.mvc.support.RedirectModel; + +/** + * Resolves {@link RedirectModel} method arguments. + * + *
This resolver must be listed ahead of the {@link ModelMethodProcessor}, + * which also resolves arguments of type {@link Model}. + * + * @author Rossen Stoyanchev + * @since 3.1 + */ +public class RedirectModelMethodArgumentResolver implements HandlerMethodArgumentResolver { + + public boolean supportsParameter(MethodParameter parameter) { + return RedirectModel.class.equals(parameter.getParameterType()); + } + + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) throws Exception { + DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null); + ModelMap implicitModel = mavContainer.getModel(); + RedirectModel redirectModel = new RedirectModel(dataBinder, implicitModel); + mavContainer.setRedirectModel(redirectModel); + return redirectModel; + } + +} 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 e634d08961..bd90fe91a9 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 @@ -16,8 +16,6 @@ package org.springframework.web.servlet.mvc.method.annotation.support; -import java.lang.reflect.Method; - import org.springframework.core.MethodParameter; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ResponseBody; @@ -26,18 +24,20 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.RequestToViewNameTranslator; import org.springframework.web.servlet.View; -import org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator; /** - * Handles return values that are of type {@code void}, {@code String} (i.e. logical view name), or {@link View}. + * Handles return values that are of type {@code void}, {@code String} (i.e. + * logical view name), or {@link View}. * - *
A {@code null} return value, either due to a void return type or as the actual value returned from a - * method is left unhandled, leaving it to the configured {@link RequestToViewNameTranslator} to resolve the - * request to an actual view name. By default it is the {@link DefaultRequestToViewNameTranslator}. + *
A {@code null} return value, either due to a void return type or as the + * actual value returned from a method is left unhandled, leaving it to the + * configured {@link RequestToViewNameTranslator} to resolve the request to + * an actual view name. * - *
Since a {@link String} return value may handled in different ways, especially in combination with method - * annotations such as @{@link ModelAttribute} and @{@link ResponseBody}, this handler should be ordered after - * return value handlers that support method annotations. + *
Since a {@link String} return value may be handled in combination with + * method annotations such as @{@link ModelAttribute} or @{@link ResponseBody}, + * this handler should be ordered after return value handlers that support + * method annotations. * * @author Rossen Stoyanchev * @since 3.1 @@ -57,17 +57,44 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan return; } if (returnValue instanceof String) { - mavContainer.setViewName((String) returnValue); + String viewName = (String) returnValue; + mavContainer.setViewName(viewName); + if (isRedirectViewName(viewName)) { + mavContainer.useRedirectModel(); + } } else if (returnValue instanceof View){ - mavContainer.setView(returnValue); + View view = (View) returnValue; + mavContainer.setView(view); + if (isRedirectView(view)) { + mavContainer.useRedirectModel(); + } } else { // should not happen - Method method = returnType.getMethod(); - String returnTypeName = returnType.getParameterType().getName(); - throw new UnsupportedOperationException("Unknown return type: " + returnTypeName + " in method: " + method); + throw new UnsupportedOperationException("Unknown return type: " + + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } } + /** + * Whether the given view name is a redirect view reference. + * @param viewName the view name to check, never {@code null} + * @return "true" if the given view name is recognized as a redirect view + * reference; "false" otherwise. + */ + protected boolean isRedirectViewName(String viewName) { + return viewName.startsWith("redirect:"); + } + + /** + * Whether the given View instance is a redirect view. + * @param view a view instance, never {@code null} + * @return "true" if the given view is recognized as a redirect View; + * "false" otherwise. + */ + protected boolean isRedirectView(View view) { + return "RedirectView".equals(view.getClass().getSimpleName()); + } + } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/support/RedirectResponse.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/support/RedirectResponse.java deleted file mode 100644 index 4ff908bd89..0000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/support/RedirectResponse.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.web.servlet.mvc.method.support; - -import java.util.Collection; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.core.Conventions; -import org.springframework.util.Assert; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.ModelAndViewContainer; -import org.springframework.web.servlet.FlashMap; -import org.springframework.web.servlet.support.RequestContextUtils; - -/** - * Provides annotated controller methods with convenience methods for setting - * up response with a view name that will result in a redirect. - * - *
An instance of this class is obtained via {@link ResponseContext#redirect}. - * - * @author Keith Donald - * @author Rossen Stoyanchev - * - * @since 3.1 - */ -public class RedirectResponse { - - private final NativeWebRequest webRequest; - - private final ModelAndViewContainer mavContainer; - - RedirectResponse(NativeWebRequest webRequest, ModelAndViewContainer mavContainer) { - this.webRequest = webRequest; - this.mavContainer = mavContainer; - } - - /** - * Add a URI template variable to use to expand the URI template into a URL. - *
Note: URI template variables from the current - * request are automatically used when expanding the redirect URI template. - * They don't need to be added explicitly here. - */ - public RedirectResponse uriVariable(String name, Object value) { - this.mavContainer.addAttribute(name, value); - return this; - } - - /** - * Add a URI template variable to use to expand the URI template into a URL. - * The name of the variable is selected using a - * {@link org.springframework.core.Conventions#getVariableName generated name}. - *
Note: URI template variables from the current - * request are automatically used when expanding the redirect URI template. - * They don't need to be added explicitly here. - */ - public RedirectResponse uriVariable(Object value) { - this.mavContainer.addAttribute(value); - return this; - } - - /** - * Add a query parameter to append to the redirect URL. - */ - public RedirectResponse queryParam(String name, Object value) { - this.mavContainer.addAttribute(name, value); - return this; - } - - /** - * Add a query parameter to append to the redirect URL. - * The name of the parameter is selected using a - * {@link org.springframework.core.Conventions#getVariableName generated name}. - */ - public RedirectResponse queryParam(Object value) { - this.mavContainer.addAttribute(value); - return this; - } - - /** - * Add a flash attribute to save and make available in the model of the - * target controller method after the redirect. - */ - public RedirectResponse flashAttribute(String name, Object value) { - getFlashMap().put(name, value); - return this; - } - - /** - * Add a flash attribute to save and make available in the model of the - * target controller method after the redirect. - * The name of the attribute is selected using a - * {@link org.springframework.core.Conventions#getVariableName generated name}. - */ - public RedirectResponse flashAttribute(Object attributeValue) { - Assert.notNull(attributeValue, "Model object must not be null"); - if (attributeValue instanceof Collection && ((Collection) attributeValue).isEmpty()) { - return this; - } - return flashAttribute(Conventions.getVariableName(attributeValue), attributeValue); - } - - private FlashMap getFlashMap() { - HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); - return RequestContextUtils.getFlashMap(servletRequest); - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/support/ResponseContext.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/support/ResponseContext.java deleted file mode 100644 index ff2ba35f31..0000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/support/ResponseContext.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.web.servlet.mvc.method.support; - -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.ModelAndViewContainer; - -/** - * Provides annotated controllers with convenience methods for setting up view - * resolution. - * - * @author Keith Donald - * @author Rossen Stoyanchev - * - * @since 3.1 - */ -public class ResponseContext { - - private final NativeWebRequest webRequest; - - private final ModelAndViewContainer mavContainer; - - public ResponseContext(NativeWebRequest webRequest, ModelAndViewContainer mavContainer) { - this.webRequest = webRequest; - this.mavContainer = mavContainer; - } - - /** - * Set up view resolution based on the given view name and the implicit model - * of the current request. - */ - public ViewResponse view(String viewName) { - this.mavContainer.setViewName(viewName); - return new ViewResponse(this.mavContainer); - } - - /** - * Set up view resolution for a redirect. This method clears the implicit - * model. Use convenience methods on the returned {@link RedirectResponse} - * instance to add URI variables, query parameters, and flash attributes - * as necessary. - * @param redirectUri a URI template either relative to the current URL or - * absolute; do not prefix with "redirect:" - */ - public RedirectResponse redirect(String redirectUri) { - if (!redirectUri.startsWith("redirect:")) { - redirectUri = "redirect:" + redirectUri; - } - this.mavContainer.getModel().clear(); - this.mavContainer.setViewName(redirectUri); - return new RedirectResponse(this.webRequest, this.mavContainer); - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/support/ViewResponse.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/support/ViewResponse.java deleted file mode 100644 index 140abd6c66..0000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/support/ViewResponse.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.web.servlet.mvc.method.support; - -import org.springframework.web.method.support.ModelAndViewContainer; - -/** - * Provides annotated controller methods with convenience methods for setting - * up a response with a view name that does not have the "redirect:" prefix . - * - *
An instance of this class is obtained via {@link ResponseContext#view}. - * - * @author Keith Donald - * @author Rossen Stoyanchev - * - * @since 3.1 - */ -public class ViewResponse { - - private final ModelAndViewContainer mavContainer; - - ViewResponse(ModelAndViewContainer mavContainer) { - this.mavContainer = mavContainer; - } - - public ViewResponse attribute(String attributeName, Object attributeValue) { - this.mavContainer.addAttribute(attributeName, attributeValue); - return this; - } - - public ViewResponse attribute(Object attributeValue) { - this.mavContainer.addAttribute(attributeValue); - return this; - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectModel.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectModel.java new file mode 100644 index 0000000000..209e959191 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectModel.java @@ -0,0 +1,199 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.mvc.support; + +import java.beans.PropertyEditor; +import java.util.Collection; +import java.util.Map; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.format.Formatter; +import org.springframework.ui.ExtendedModelMap; +import org.springframework.ui.Model; +import org.springframework.ui.ModelMap; +import org.springframework.util.Assert; +import org.springframework.validation.DataBinder; + +/** + * A {@link Model} implementation that controllers can use when they wish to + * redirect. For a redirect a controller needs to use an empty model and + * only add those attributes that will be used in the redirect URL -- + * either embedded as URI template variables or appended as query parameters. + * To be used in the URL such attributes need to be formatted as String + * values. Alternatively a controller may choose to keep attributes in + * flash storage instead for the duration of the redirect. + * + *
A RedirectModel serves the above needs as follows: + *
Note that a RedirectModel will not be used unless the controller decides
+ * to redirect.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.1
+ */
+@SuppressWarnings("serial")
+public class RedirectModel extends ExtendedModelMap {
+
+ private final DataBinder dataBinder;
+
+ private final ModelMap flashAttributes = new ModelMap();
+
+ private final ModelMap implicitModel;
+
+ /**
+ * Create a new instance without a DataBinder. Attribute values will be
+ * formatted via {@link #toString()}.
+ */
+ public RedirectModel() {
+ this(null, null);
+ }
+
+ /**
+ * Create a new instance providing a DataBinder to use for formatting
+ * attribute values.
+ * @param dataBinder a DataBinder for converting attribute values to String.
+ * @param implicitModel the implicit model for the current request;
+ * used in conjunction with {@link #addModelAttributes(String...)}
+ * to copy attributes from the implicit model to the redirect model.
+ */
+ public RedirectModel(DataBinder dataBinder, ModelMap implicitModel) {
+ this.dataBinder = dataBinder;
+ this.implicitModel = implicitModel;
+ }
+
+ /**
+ * Return the attributes candidate for flash storage.
+ */
+ public Map A typical scenario begins with a controller adding attributes to the {@link Model}.
- * At the end of the request, model attributes are checked to see if any of them match
- * the names and types declared via @{@link SessionAttributes}. Matching model
- * attributes are "promoted" to the session and remain there until the controller
- * calls {@link SessionStatus#setComplete()} to indicate the session attributes are
+ * A typical scenario begins with a controller adding attributes to the
+ * {@link org.springframework.ui.Model Model}. At the end of the request, model
+ * attributes are checked to see if any of them match the names and types declared
+ * via @{@link SessionAttributes}. Matching model attributes are "promoted" to
+ * the session and remain there until the controller calls
+ * {@link SessionStatus#setComplete()} to indicate the session attributes are
* no longer needed and can be removed.
*
* @author Rossen Stoyanchev
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 999fb73d7c..c1ce67059e 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
@@ -35,9 +35,13 @@ public class ModelAndViewContainer {
private Object view;
private boolean resolveView = true;
-
- private final ModelMap model = new BindingAwareModelMap();
+ private final ModelMap model = new BindingAwareModelMap();
+
+ private ModelMap redirectModel;
+
+ private boolean useRedirectModel = false;
+
/**
* Create a new instance.
*/
@@ -104,10 +108,35 @@ public class ModelAndViewContainer {
}
/**
- * Return the underlying {@code ModelMap} instance, never {@code null}.
+ * Return the internal model currently in use. Normally this is the
+ * implicit model created in the constructor of this class.
+ * However, when a redirect model is provided via {@link #setRedirectModel}
+ * and then enabled for use via {@link #useRedirectModel()}, this method
+ * returns the redirect model instead.
+ *
+ * @return the internal model currently in use, never {@code null}.
*/
public ModelMap getModel() {
- return this.model;
+ return this.useRedirectModel ? this.redirectModel : this.model;
+ }
+
+ /**
+ * Provide a model that may be used in case of a redirect.
+ * If {@link #useRedirectModel()} is called at any time after this method is
+ * called, the ModelAndViewContainer will switch to using the redirect model
+ * for all operations. Until then, the redirect model is not used.
+ */
+ public void setRedirectModel(ModelMap redirectModel) {
+ this.redirectModel = redirectModel;
+ }
+
+ /**
+ * Make a switch internally from using the implicit model to using the
+ * redirect model provided earlier via {@link #setRedirectModel(ModelMap)}.
+ * If the redirect model was never set, this method has no effect.
+ */
+ public void useRedirectModel() {
+ this.useRedirectModel = this.redirectModel != null;
}
/**
@@ -115,7 +144,7 @@ public class ModelAndViewContainer {
* @see ModelMap#addAttribute(String, Object)
*/
public ModelAndViewContainer addAttribute(String name, Object value) {
- this.model.addAttribute(name, value);
+ getModel().addAttribute(name, value);
return this;
}
@@ -124,7 +153,7 @@ public class ModelAndViewContainer {
* @see Model#addAttribute(Object)
*/
public ModelAndViewContainer addAttribute(Object value) {
- this.model.addAttribute(value);
+ getModel().addAttribute(value);
return this;
}
@@ -133,7 +162,7 @@ public class ModelAndViewContainer {
* @see ModelMap#addAllAttributes(Map)
*/
public ModelAndViewContainer addAllAttributes(MapCollection into this
+ * Model using attribute name generation for each element.
+ * @see #addAttribute(Object)
+ */
+ public RedirectModel addAllAttributes(Collection> attributeValues) {
+ super.addAllAttributes(attributeValues);
+ return this;
+ }
+
+ /**
+ * Copy all supplied attributes into this redirect model.
+ * @see #addAttribute(String, Object)
+ */
+ public RedirectModel addAllAttributes(Map