diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index 1f7ebf7972..1875eead59 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -671,7 +671,8 @@ public class DispatcherServlet extends FrameworkServlet { /** * Initialize the {@link FlashMapManager} used by this servlet instance. - *
If no implementation is configured then we default to DefaultFlashMapManager. + *
If no implementation is configured then we default to + * {@code org.springframework.web.servlet.support.DefaultFlashMapManager}. */ private void initFlashMapManager(ApplicationContext context) { try { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMap.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMap.java index 5e80f0f363..72f2155b11 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMap.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FlashMap.java @@ -21,7 +21,6 @@ import java.util.LinkedHashMap; import java.util.Map; import org.springframework.beans.BeanUtils; -import org.springframework.util.Assert; /** * A FlashMap provides a way for one request to store attributes intended for @@ -33,11 +32,14 @@ import org.springframework.util.Assert; *
A FlashMap can be set up with a request path and request parameters to * help identify the target request. Without this information, a FlashMap is * made available to the next request, which may or may not be the intended - * result. Before a redirect, the target URL is known and when using the - * {@code org.springframework.web.servlet.view.RedirectView}, FlashMap - * instances are automatically updated with redirect URL information. + * recipient. On a redirect, the target URL is known and for example + * {@code org.springframework.web.servlet.view.RedirectView} has the + * opportunity to automatically update the current FlashMap with target + * URL information . * - *
Annotated controllers will usually not access a FlashMap directly.. TODO + *
Annotated controllers will usually not use this type directly.
+ * See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}
+ * for an overview of using flash attributes in annotated controllers.
*
* @author Rossen Stoyanchev
* @since 3.1
@@ -58,13 +60,6 @@ public class FlashMap extends HashMap A FlashMapManager is invoked at the beginning and at the end of a request.
- * For each request, it exposes an "input" FlashMap with attributes passed from
- * a previous request (if any) and an "output" FlashMap with attributes to pass
- * to a subsequent request. Both FlashMap instances are exposed via request
- * attributes and can be accessed through methods in
- * {@code org.springframework.web.servlet.support.RequestContextUtils}.
+ * Annotated controllers are most likely to store and access flash attributes
+ * through their model.
+ * See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}.
*
* @author Rossen Stoyanchev
* @since 3.1
@@ -39,42 +41,39 @@ import javax.servlet.http.HttpServletRequest;
public interface FlashMapManager {
/**
- * Name of request attribute that holds a read-only {@link Map} with
- * "input" flash attributes from a previous request (if any).
+ * Name of request attribute that holds a read-only
+ * {@code Map The "output" FlashMap is not saved if it is empty or if it was not
- * created by this FlashMapManager.
+ * Start the expiration period of the "output" FlashMap save it in the
+ * underlying storage.
+ * The "output" FlashMap should not be saved if it is empty or if it was
+ * not created by the current FlashMapManager instance.
+ * @param request the current request
*/
void requestCompleted(HttpServletRequest request);
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/SmartView.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/SmartView.java
index 55dcdb21d8..3ac6526f66 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/SmartView.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/SmartView.java
@@ -17,7 +17,8 @@
package org.springframework.web.servlet;
/**
- * Interface to be implemented by Views that perform a redirect.
+ * Provides additional information about a View such as whether it
+ * performs redirects.
*
* @author Rossen Stoyanchev
* @since 3.1
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 caf8749e61..dc878feaca 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
@@ -31,10 +31,10 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
/**
- * Resolves {@link RedirectAttributesModelMap} method arguments.
+ * Resolves method arguments of type {@link RedirectAttributes}.
*
- * This resolver must be listed ahead of the {@link ModelMethodProcessor},
- * which also resolves arguments of type {@link Map} and {@link Model}.
+ * This resolver must be listed before the {@link ModelMethodProcessor},
+ * which resolves {@link Map} and {@link Model} arguments.
*
* @author Rossen Stoyanchev
* @since 3.1
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributes.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributes.java
index 11e6f07742..682c7964d7 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributes.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/support/RedirectAttributes.java
@@ -20,70 +20,66 @@ import java.util.Collection;
import java.util.Map;
import org.springframework.ui.Model;
+import org.springframework.web.servlet.FlashMap;
/**
- * A {@link Model} that can also store attributes candidate for flash storage.
+ * A specialization of the {@link Model} interface that controllers can use to
+ * select attributes for a redirect scenario. Since the intent of adding
+ * redirect attributes is very explicit -- i.e. to be used for a redirect URL,
+ * attribute values may be formatted as Strings and stored that way to make
+ * them eligible to be appended to the query string or expanded as URI
+ * variables in {@code org.springframework.web.servlet.view.RedirectView}.
+ *
+ * This interface also provides a way to add flash attributes. For a
+ * general overview of flash attributes see {@link FlashMap}. You can use
+ * {@link RedirectAttributes} to store flash attributes and they will be
+ * automatically propagated to the "output" FlashMap of the current request.
+ *
+ * Example usage in an {@code @Controller}:
+ * After the redirect, flash attributes are automatically added to the model
+ * of the controller serving the redirect URL.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
public interface RedirectAttributes extends Model {
- /**
- * Add the supplied attribute under the supplied name.
- * @param attributeName the name of the model attribute (never Attributes are formatted and stored as Strings so they can be used as URI
- * variables or as query parameters in the redirect URL. Alternatively,
- * attributes may also be added as flash attributes in order to request storing
- * them until the next request without affecting the redirect URL.
+ * A {@link ModelMap} implementation of {@link RedirectAttributes} that formats
+ * values as Strings using a {@link DataBinder}. Also provides a place to store
+ * flash attributes so they can survive a redirect without the need to be
+ * embedded in the redirect URL.
*
* @author Rossen Stoyanchev
* @since 3.1
@@ -41,33 +39,31 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
private final ModelMap flashAttributes = new ModelMap();
/**
- * Default constructor without a DataBinder.
- * Redirect attribute values will be formatted via {@link #toString()}.
+ * Class constructor.
+ * @param dataBinder used to format attribute values as Strings.
*/
- public RedirectAttributesModelMap() {
- this(null);
+ public RedirectAttributesModelMap(DataBinder dataBinder) {
+ this.dataBinder = dataBinder;
}
-
+
/**
- * Constructor with a DataBinder to use to format redirect attribute values.
- * @param dataBinder a DataBinder for converting attribute values to String.
+ * Default constructor without a DataBinder.
+ * Attribute values are converted to String via {@link #toString()}.
*/
- public RedirectAttributesModelMap(DataBinder dataBinder) {
- this.dataBinder = dataBinder;
+ public RedirectAttributesModelMap() {
+ this(null);
}
/**
- * Return the attributes candidate for flash storage.
+ * Return the attributes candidate for flash storage or an empty Map.
*/
public Map Formats the attribute value as a String before adding it.
*/
public RedirectAttributesModelMap addAttribute(String attributeName, Object attributeValue) {
if (attributeValue != null) {
@@ -81,10 +77,8 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
}
/**
- * Add an attribute using a
- * {@link org.springframework.core.Conventions#getVariableName generated name}.
- * Before being added the attribute value is formatted as a String.
- * @param attributeValue the attribute value; never null
+ * {@inheritDoc}
+ * Formats the attribute value as a String before adding it.
*/
public RedirectAttributesModelMap addAttribute(Object attributeValue) {
super.addAttribute(attributeValue);
@@ -92,9 +86,8 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
}
/**
- * Copy all attributes in the supplied Each attribute value is formatted as a String before being added.
*/
public RedirectAttributesModelMap addAllAttributes(Collection> attributeValues) {
super.addAllAttributes(attributeValues);
@@ -102,8 +95,8 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
}
/**
- * Copy all supplied attributes into this model.
- * @see #addAttribute(String, Object)
+ * {@inheritDoc}
+ * Each attribute value is formatted as a String before being added.
*/
public RedirectAttributesModelMap addAllAttributes(Map Each attribute value is formatted as a String before being merged.
*/
public RedirectAttributesModelMap mergeAttributes(Map The default value is 180 seconds.
*/
public void setFlashMapTimeout(int flashTimeout) {
this.flashTimeout = flashTimeout;
@@ -59,16 +60,16 @@ public class DefaultFlashMapManager implements FlashMapManager {
/**
* {@inheritDoc}
- * This method never causes the HTTP session to be created.
+ * An HTTP session is never created by this method.
*/
public void requestStarted(HttpServletRequest request) {
if (request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE) != null) {
return;
}
- Map An HTTP session is never created if the "output" FlashMap is empty.
+ */
public void requestCompleted(HttpServletRequest request) {
FlashMap flashMap = (FlashMap) request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE);
if (flashMap == null) {
- throw new IllegalStateException(
- "Did not find a FlashMap exposed as the request attribute " + OUTPUT_FLASH_MAP_ATTRIBUTE);
+ throw new IllegalStateException("requestCompleted called but \"output\" FlashMap was never created");
}
if (!flashMap.isEmpty() && flashMap.isCreatedBy(this.hashCode())) {
if (logger.isDebugEnabled()) {
logger.debug("Saving FlashMap=" + flashMap);
}
- decodeAndNormalizeTargetPath(flashMap, request);
- flashMap.startExpirationPeriod(this.flashTimeout);
+ onSaveFlashMap(flashMap, request);
retrieveFlashMaps(request, true).add(flashMap);
}
}
-
+
/**
- * Ensure the target request path in the given FlashMap is decoded and also
- * normalized (if it is relative) against the current request URL.
+ * Update a FlashMap before it is stored in the HTTP Session.
+ * The default implementation starts the expiration period and ensures the
+ * target request path is decoded and normalized if it is relative.
+ * @param flashMap the flash map to be saved
+ * @param request the current request
*/
- private void decodeAndNormalizeTargetPath(FlashMap flashMap, HttpServletRequest request) {
- String path = flashMap.getTargetRequestPath();
+ protected void onSaveFlashMap(FlashMap flashMap, HttpServletRequest request) {
+ String targetPath = flashMap.getTargetRequestPath();
+ flashMap.setTargetRequestPath(decodeAndNormalizePath(targetPath, request));
+ flashMap.startExpirationPeriod(this.flashTimeout);
+ }
+
+ private String decodeAndNormalizePath(String path, HttpServletRequest request) {
if (path != null) {
- path = urlPathHelper.decodeRequestString(request, path);
+ path = this.urlPathHelper.decodeRequestString(request, path);
if (path.charAt(0) != '/') {
String requestUri = this.urlPathHelper.getRequestUri(request);
path = requestUri.substring(0, requestUri.lastIndexOf('/') + 1) + path;
path = StringUtils.cleanPath(path);
}
- flashMap.setTargetRequestPath(path);
}
+ return path;
}
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/RequestContextUtils.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/RequestContextUtils.java
index 62497fe263..a615777b88 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/RequestContextUtils.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/RequestContextUtils.java
@@ -157,9 +157,11 @@ public abstract class RequestContextUtils {
}
/**
- * Return a read-only Map with flash attributes saved during the previous request.
+ * Return a read-only {@link Map} with "input" flash attributes saved on a
+ * previous request.
* @param request the current request
- * @return a read-only Map, or {@code null}
+ * @return a read-only Map, or {@code null}
+ * @see FlashMap
*/
@SuppressWarnings("unchecked")
public static Map
- *
- *
* @param request the current request
*/
void requestStarted(HttpServletRequest request);
/**
- * Save the "output" FlashMap in underlying storage, start its expiration
- * period, and decode/normalize its target request path.
- *
- *
+ * @RequestMapping(value = "/accounts", method = RequestMethod.POST)
+ * public String handle(Account account, BindingResult result, RedirectAttributes redirectAttrs) {
+ * if (result.hasErrors()) {
+ * return "accounts/new";
+ * }
+ * // Save account ...
+ * redirectAttrs.addAttribute("id", account.getId()).addFlashAttribute("message", "Account created!");
+ * return "redirect:/accounts/{id}";
+ * }
+ *
+ *
+ * null
)
- * @param attributeValue the model attribute value (can be null
)
- */
RedirectAttributes addAttribute(String attributeName, Object attributeValue);
- /**
- * Add the supplied attribute to this Map
using a
- * {@link org.springframework.core.Conventions#getVariableName generated name}.
- * null
rather
- * than for empty collections as is already done by JSTL tags.null
)
- */
RedirectAttributes addAttribute(Object attributeValue);
- /**
- * Copy all attributes in the supplied Collection
into this
- * Map
, using attribute name generation for each element.
- * @see #addAttribute(Object)
- */
RedirectAttributes addAllAttributes(Collection> attributeValues);
-
- /**
- * Copy all attributes in the supplied Map
into this Map
.
- * @see #addAttribute(String, Object)
- */
- Model addAllAttributes(MapMap
into this Map
,
- * with existing objects of the same name taking precedence (i.e. not getting
- * replaced).
- */
- RedirectAttributes mergeAttributes(MapCollection
into this
- * Model using attribute name generation for each element.
- * @see #addAttribute(Object)
+ * {@inheritDoc}
+ *