SPR-6464 Drop @FlashAttributes, add ResponseContext, ViewResponse, and RedirectResponse types for annotated controllers to use to prepare a redirect response with flash attributes; Add FlashMap and FlashMapManager and update DispatcherServlet to discover and invoke the FlashMapManager.
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation that indicates what attributes should be stored in the session or in
|
||||
* some conversational storage in order to survive a client-side redirect.
|
||||
*
|
||||
* TODO ...
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @see org.springframework.web.bind.support.FlashStatus
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface FlashAttributes {
|
||||
|
||||
/**
|
||||
* The names of flash attributes in the model to be stored.
|
||||
*
|
||||
* TODO ...
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* TODO ...
|
||||
*
|
||||
*/
|
||||
Class[] types() default {};
|
||||
|
||||
}
|
||||
@@ -1,51 +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.bind.support;
|
||||
|
||||
import org.springframework.web.bind.annotation.FlashAttributes;
|
||||
|
||||
|
||||
/**
|
||||
* Simple interface to pass into controller methods to allow them to activate
|
||||
* a mode in which model attributes identified as "flash attributes" are
|
||||
* temporarily stored in the session to make them available to the next
|
||||
* request. The most common scenario is a client-side redirect.
|
||||
*
|
||||
* <p>In active mode, model attributes that match the attribute names or
|
||||
* types declared via @{@link FlashAttributes} are saved in the session.
|
||||
* On the next request, any flash attributes found in the session are
|
||||
* automatically added to the model of the target controller method and
|
||||
* are also cleared from the session.
|
||||
*
|
||||
* TODO ...
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface FlashStatus {
|
||||
|
||||
/**
|
||||
* TODO ...
|
||||
*/
|
||||
void setActive();
|
||||
|
||||
/**
|
||||
* TODO ...
|
||||
*/
|
||||
boolean isActive();
|
||||
|
||||
}
|
||||
@@ -1,37 +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.bind.support;
|
||||
|
||||
/**
|
||||
* TODO ...
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SimpleFlashStatus implements FlashStatus {
|
||||
|
||||
private boolean active = false;
|
||||
|
||||
public void setActive() {
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,108 +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.method.annotation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.web.bind.annotation.FlashAttributes;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* Manages flash attributes declared via @{@link FlashAttributes}.
|
||||
*
|
||||
* TODO ...
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FlashAttributesHandler {
|
||||
|
||||
public static final String FLASH_ATTRIBUTES_SESSION_KEY = FlashAttributesHandler.class.getName() + ".attributes";
|
||||
|
||||
private final Set<String> attributeNames = new HashSet<String>();
|
||||
|
||||
private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
|
||||
|
||||
/**
|
||||
* TODO ...
|
||||
*/
|
||||
public FlashAttributesHandler(Class<?> handlerType) {
|
||||
FlashAttributes annotation = AnnotationUtils.findAnnotation(handlerType, FlashAttributes.class);
|
||||
if (annotation != null) {
|
||||
this.attributeNames.addAll(Arrays.asList(annotation.value()));
|
||||
this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the controller represented by this handler has declared flash
|
||||
* attribute names or types via @{@link FlashAttributes}.
|
||||
*/
|
||||
public boolean hasFlashAttributes() {
|
||||
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO ...
|
||||
*/
|
||||
public boolean isFlashAttribute(String attributeName, Class<?> attributeType) {
|
||||
return (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO ...
|
||||
*/
|
||||
public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
|
||||
Map<String, Object> filtered = filterAttributes(attributes);
|
||||
if (!filtered.isEmpty()) {
|
||||
request.setAttribute(FLASH_ATTRIBUTES_SESSION_KEY, filtered, WebRequest.SCOPE_SESSION);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> filterAttributes(Map<String, ?> attributes) {
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>();
|
||||
for (String name : attributes.keySet()) {
|
||||
Object value = attributes.get(name);
|
||||
Class<?> type = (value != null) ? value.getClass() : null;
|
||||
if (isFlashAttribute(name, type)) {
|
||||
result.put(name, value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO ...
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> retrieveAttributes(WebRequest request) {
|
||||
return (Map<String, Object>) request.getAttribute(FLASH_ATTRIBUTES_SESSION_KEY, WebRequest.SCOPE_SESSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO ...
|
||||
*/
|
||||
public void cleanupAttributes(WebRequest request) {
|
||||
request.removeAttribute(FLASH_ATTRIBUTES_SESSION_KEY, WebRequest.SCOPE_SESSION);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,7 +35,6 @@ import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.FlashStatus;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
@@ -66,23 +65,18 @@ public final class ModelFactory {
|
||||
|
||||
private final SessionAttributesHandler sessionAttributesHandler;
|
||||
|
||||
private final FlashAttributesHandler flashAttributesHandler;
|
||||
|
||||
/**
|
||||
* Create a ModelFactory instance with the provided {@link ModelAttribute} methods.
|
||||
* @param attributeMethods {@link ModelAttribute} methods to initialize model instances with
|
||||
* @param binderFactory used to add {@link BindingResult} attributes to the model
|
||||
* @param sessionAttributesHandler used to access handler-specific session attributes
|
||||
* @param flashAttributesHandler used to access flash attributes
|
||||
*/
|
||||
public ModelFactory(List<InvocableHandlerMethod> attributeMethods,
|
||||
WebDataBinderFactory binderFactory,
|
||||
SessionAttributesHandler sessionAttributesHandler,
|
||||
FlashAttributesHandler flashAttributesHandler) {
|
||||
SessionAttributesHandler sessionAttributesHandler) {
|
||||
this.attributeMethods = (attributeMethods != null) ? attributeMethods : new ArrayList<InvocableHandlerMethod>();
|
||||
this.binderFactory = binderFactory;
|
||||
this.sessionAttributesHandler = sessionAttributesHandler;
|
||||
this.flashAttributesHandler = flashAttributesHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,21 +87,17 @@ public final class ModelFactory {
|
||||
* <li>Check the session for any controller-specific attributes not yet "remembered".
|
||||
* </ol>
|
||||
* @param request the current request
|
||||
* @param mavContainer contains the model to initialize
|
||||
* @param mavContainer contains the model to initialize
|
||||
* @param handlerMethod the @{@link RequestMapping} method for which the model is initialized
|
||||
* @throws Exception may arise from the invocation of @{@link ModelAttribute} methods
|
||||
*/
|
||||
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
|
||||
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
|
||||
throws Exception {
|
||||
|
||||
|
||||
Map<String, ?> sessionAttrs = this.sessionAttributesHandler.retrieveAttributes(request);
|
||||
mavContainer.addAllAttributes(sessionAttrs);
|
||||
mavContainer.mergeAttributes(sessionAttrs);
|
||||
|
||||
Map<String, ?> flashAttrs = this.flashAttributesHandler.retrieveAttributes(request);
|
||||
mavContainer.addAllAttributes(flashAttrs);
|
||||
this.flashAttributesHandler.cleanupAttributes(request);
|
||||
|
||||
invokeAttributeMethods(request, mavContainer);
|
||||
invokeModelAttributeMethods(request, mavContainer);
|
||||
|
||||
checkHandlerSessionAttributes(request, mavContainer, handlerMethod);
|
||||
}
|
||||
@@ -116,7 +106,7 @@ public final class ModelFactory {
|
||||
* Invoke model attribute methods to populate the model.
|
||||
* If two methods return the same attribute, the attribute from the first method is added.
|
||||
*/
|
||||
private void invokeAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
|
||||
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
|
||||
throws Exception {
|
||||
|
||||
for (InvocableHandlerMethod attrMethod : this.attributeMethods) {
|
||||
@@ -128,18 +118,23 @@ public final class ModelFactory {
|
||||
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
|
||||
|
||||
if (!attrMethod.isVoid()){
|
||||
String valueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
|
||||
mavContainer.mergeAttribute(valueName, returnValue);
|
||||
String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
|
||||
if (!mavContainer.containsAttribute(returnValueName)) {
|
||||
mavContainer.addAttribute(returnValueName, returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any @{@link ModelAttribute} handler method arguments declared as
|
||||
* session attributes via @{@link SessionAttributes} but are not already in the
|
||||
* model. If found add them to the model, raise an exception otherwise.
|
||||
* Checks for @{@link ModelAttribute} arguments in the signature of the
|
||||
* {@link RequestMapping} method that are declared as session attributes
|
||||
* via @{@link SessionAttributes} but are not already in the model.
|
||||
* Those attributes may have been outside of this controller.
|
||||
* Try to locate the attributes in the session or raise an exception.
|
||||
*
|
||||
* @throws HttpSessionRequiredException raised if a handler session attribute could is missing
|
||||
* @throws HttpSessionRequiredException raised if an attribute declared
|
||||
* as session attribute is missing.
|
||||
*/
|
||||
private void checkHandlerSessionAttributes(NativeWebRequest request,
|
||||
ModelAndViewContainer mavContainer,
|
||||
@@ -203,11 +198,11 @@ public final class ModelFactory {
|
||||
* promotes model attributes to the session, and adds {@link BindingResult} attributes where missing.
|
||||
* @param request the current request
|
||||
* @param mavContainer the {@link ModelAndViewContainer} for the current request
|
||||
* @param sessionStatus whether session processing is complete
|
||||
* @param sessionStatus the session status for the current request
|
||||
* @throws Exception if the process of creating {@link BindingResult} attributes causes an error
|
||||
*/
|
||||
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer,
|
||||
SessionStatus sessionStatus, FlashStatus flashStatus) throws Exception {
|
||||
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer, SessionStatus sessionStatus)
|
||||
throws Exception {
|
||||
|
||||
if (sessionStatus.isComplete()){
|
||||
this.sessionAttributesHandler.cleanupAttributes(request);
|
||||
@@ -215,10 +210,6 @@ public final class ModelFactory {
|
||||
else {
|
||||
this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
|
||||
}
|
||||
|
||||
if (flashStatus.isActive()) {
|
||||
this.flashAttributesHandler.storeAttributes(request, mavContainer.getModel());
|
||||
}
|
||||
|
||||
if (mavContainer.isResolveView()) {
|
||||
updateBindingResult(request, mavContainer.getModel());
|
||||
|
||||
@@ -95,7 +95,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
String name = ModelFactory.getNameForParameter(parameter);
|
||||
Object target = (mavContainer.containsAttribute(name)) ?
|
||||
mavContainer.getAttribute(name) : createAttribute(name, parameter, binderFactory, request);
|
||||
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
|
||||
|
||||
WebDataBinder binder = binderFactory.createBinder(request, target, name);
|
||||
|
||||
|
||||
@@ -56,9 +56,7 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
Class<?> paramType = returnType.getParameterType();
|
||||
boolean hasModelAttr = returnType.getMethodAnnotation(ModelAttribute.class) != null;
|
||||
|
||||
return (Model.class.isAssignableFrom(paramType)
|
||||
|| (Map.class.isAssignableFrom(paramType) && !hasModelAttr));
|
||||
return (Model.class.isAssignableFrom(paramType) || (Map.class.isAssignableFrom(paramType) && !hasModelAttr));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
|
||||
@@ -18,142 +18,141 @@ package org.springframework.web.method.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.support.BindingAwareModelMap;
|
||||
|
||||
/**
|
||||
* Provides access to the model and a place to record model and view related decisions made by
|
||||
* {@link HandlerMethodArgumentResolver}s or a {@link HandlerMethodReturnValueHandler}.
|
||||
*
|
||||
* <p>In addition to storing model attributes and a view, the {@link ModelAndViewContainer} also provides
|
||||
* a {@link #setResolveView(boolean)} flag, which can be used to request or bypass a view resolution phase.
|
||||
* This is most commonly used from {@link HandlerMethodReturnValueHandler}s but in some cases may also be
|
||||
* used from {@link HandlerMethodArgumentResolver}s such as when a handler method accepts an argument
|
||||
* providing access to the response. When that is the case, if the handler method returns {@code null},
|
||||
* view resolution is skipped.
|
||||
* 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.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ModelAndViewContainer {
|
||||
|
||||
private String viewName;
|
||||
|
||||
private Object view;
|
||||
|
||||
private final ModelMap model;
|
||||
|
||||
private boolean resolveView = true;
|
||||
|
||||
private final ModelMap model = new BindingAwareModelMap();
|
||||
|
||||
/**
|
||||
* Create a {@link ModelAndViewContainer} instance with a {@link BindingAwareModelMap}.
|
||||
* Create a new instance.
|
||||
*/
|
||||
public ModelAndViewContainer() {
|
||||
this.model = new BindingAwareModelMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link ModelAndViewContainer} instance with the given {@link ModelMap} instance.
|
||||
* @param model the model to use
|
||||
*/
|
||||
public ModelAndViewContainer(ModelMap model) {
|
||||
Assert.notNull(model);
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the model for the current request
|
||||
*/
|
||||
public ModelMap getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the view name to use for view resolution, or {@code null}
|
||||
*/
|
||||
public String getViewName() {
|
||||
return this.viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param viewName the name of the view to use for view resolution
|
||||
* Set a view name to be resolved by the DispatcherServlet via a ViewResolver.
|
||||
* Will override any pre-existing view name or View.
|
||||
*/
|
||||
public void setViewName(String viewName) {
|
||||
this.viewName = viewName;
|
||||
this.view = viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the view instance to use for view resolution
|
||||
* Return the view name to be resolved by the DispatcherServlet via a
|
||||
* ViewResolver, or {@code null} if a View object is set.
|
||||
*/
|
||||
public Object getView() {
|
||||
return this.view;
|
||||
public String getViewName() {
|
||||
return (this.view instanceof String ? (String) this.view : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param view the view instance to use for view resolution
|
||||
* Set a View object to be used by the DispatcherServlet.
|
||||
* Will override any pre-existing view name or View.
|
||||
*/
|
||||
public void setView(Object view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return whether the view resolution is requested ({@code true}), or should be bypassed ({@code false})
|
||||
* Return the View object, or {@code null} if we using a view name
|
||||
* to be resolved by the DispatcherServlet via a ViewResolver.
|
||||
*/
|
||||
public boolean isResolveView() {
|
||||
return resolveView;
|
||||
public Object getView() {
|
||||
return this.view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resolveView whether the view resolution is requested ({@code true}), or should be bypassed ({@code false})
|
||||
* Whether the view is a view reference specified via a name to be
|
||||
* resolved by the DispatcherServlet via a ViewResolver.
|
||||
*/
|
||||
public boolean isViewReference() {
|
||||
return (this.view instanceof String);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether view resolution is required or not. The default value is "true".
|
||||
* <p>When set to "false" by a {@link HandlerMethodReturnValueHandler}, the response
|
||||
* is considered complete and view resolution is not be performed.
|
||||
* <p>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.
|
||||
*/
|
||||
public void setResolveView(boolean resolveView) {
|
||||
this.resolveView = resolveView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether view resolution is required or not.
|
||||
*/
|
||||
public boolean isResolveView() {
|
||||
return this.resolveView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether model contains an attribute of the given name.
|
||||
* @param name the name of the model attribute
|
||||
* @return {@code true} if the model contains an attribute by that name and the name is not an empty string
|
||||
* Return the underlying {@code ModelMap} instance, never {@code null}.
|
||||
*/
|
||||
public ModelMap getModel() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the supplied attribute to the underlying model.
|
||||
* @see ModelMap#addAttribute(String, Object)
|
||||
*/
|
||||
public ModelAndViewContainer addAttribute(String name, Object value) {
|
||||
this.model.addAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the supplied attribute to the underlying model.
|
||||
* @see Model#addAttribute(Object)
|
||||
*/
|
||||
public ModelAndViewContainer addAttribute(Object value) {
|
||||
this.model.addAttribute(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all attributes to the underlying model.
|
||||
* @see ModelMap#addAllAttributes(Map)
|
||||
*/
|
||||
public ModelAndViewContainer addAllAttributes(Map<String, ?> attributes) {
|
||||
this.model.addAllAttributes(attributes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy attributes in the supplied <code>Map</code> with existing objects of
|
||||
* the same name taking precedence (i.e. not getting replaced).
|
||||
* @see ModelMap#mergeAttributes(Map)
|
||||
*/
|
||||
public ModelAndViewContainer mergeAttributes(Map<String, ?> attributes) {
|
||||
this.model.mergeAttributes(attributes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the underlying model contains the given attribute name.
|
||||
* @see ModelMap#containsAttribute(String)
|
||||
*/
|
||||
public boolean containsAttribute(String name) {
|
||||
return (StringUtils.hasText(name) && model.containsAttribute(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the attribute to get from the model
|
||||
* @return the attribute or {@code null}
|
||||
*/
|
||||
public Object getAttribute(String name) {
|
||||
return model.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the supplied attribute under the given name.
|
||||
* @param name the name of the model attribute (never null)
|
||||
* @param value the model attribute value (can be null)
|
||||
*/
|
||||
public void addAttribute(String name, Object value) {
|
||||
model.addAttribute(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all attributes in the supplied Map into the model
|
||||
*/
|
||||
public void addAllAttributes(Map<String, ?> attributes) {
|
||||
model.addAllAttributes(attributes);
|
||||
return this.model.containsAttribute(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given attribute if the model does not already contain such an attribute.
|
||||
* @param name the name of the attribute to check and add
|
||||
* @param value the value of the attribute
|
||||
*/
|
||||
public void mergeAttribute(String name, Object value) {
|
||||
if (!containsAttribute(name)) {
|
||||
model.addAttribute(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SimpleFlashStatus;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
@@ -66,12 +66,8 @@ public class ModelFactoryTests {
|
||||
|
||||
private SessionAttributesHandler sessionAttrsHandler;
|
||||
|
||||
private FlashAttributesHandler flashAttrsHandler;
|
||||
|
||||
private SessionAttributeStore sessionAttributeStore;
|
||||
|
||||
private ModelAndViewContainer mavContainer;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
@@ -82,42 +78,44 @@ public class ModelFactoryTests {
|
||||
handleSessionAttrMethod = new InvocableHandlerMethod(handler, method);
|
||||
sessionAttributeStore = new DefaultSessionAttributeStore();
|
||||
sessionAttrsHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
|
||||
flashAttrsHandler = new FlashAttributesHandler(handlerType);
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modelAttributeMethod() throws Exception {
|
||||
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||
|
||||
assertEquals(Boolean.TRUE, mavContainer.getAttribute("modelAttr"));
|
||||
assertEquals(Boolean.TRUE, mavContainer.getModel().get("modelAttr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modelAttributeMethodWithSpecifiedName() throws Exception {
|
||||
ModelFactory modelFactory = createModelFactory("modelAttrWithName");
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||
|
||||
assertEquals(Boolean.TRUE, mavContainer.getAttribute("name"));
|
||||
assertEquals(Boolean.TRUE, mavContainer.getModel().get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modelAttributeMethodWithNameByConvention() throws Exception {
|
||||
ModelFactory modelFactory = createModelFactory("modelAttrConvention");
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||
|
||||
assertEquals(Boolean.TRUE, mavContainer.getAttribute("boolean"));
|
||||
assertEquals(Boolean.TRUE, mavContainer.getModel().get("boolean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modelAttributeMethodWithNullReturnValue() throws Exception {
|
||||
ModelFactory modelFactory = createModelFactory("nullModelAttr");
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||
|
||||
assertTrue(mavContainer.containsAttribute("name"));
|
||||
assertNull(mavContainer.getAttribute("name"));
|
||||
assertNull(mavContainer.getModel().get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -128,30 +126,33 @@ public class ModelFactoryTests {
|
||||
assertTrue(sessionAttrsHandler.isHandlerSessionAttribute("sessionAttr", null));
|
||||
|
||||
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||
|
||||
assertEquals("sessionAttrValue", mavContainer.getAttribute("sessionAttr"));
|
||||
assertEquals("sessionAttrValue", mavContainer.getModel().get("sessionAttr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requiredSessionAttribute() throws Exception {
|
||||
ModelFactory modelFactory = new ModelFactory(null, null, sessionAttrsHandler, flashAttrsHandler);
|
||||
ModelFactory modelFactory = new ModelFactory(null, null, sessionAttrsHandler);
|
||||
|
||||
try {
|
||||
modelFactory.initModel(webRequest, mavContainer, handleSessionAttrMethod);
|
||||
modelFactory.initModel(webRequest, new ModelAndViewContainer(), handleSessionAttrMethod);
|
||||
fail("Expected HttpSessionRequiredException");
|
||||
} catch (HttpSessionRequiredException e) { }
|
||||
|
||||
sessionAttributeStore.storeAttribute(webRequest, "sessionAttr", "sessionAttrValue");
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
modelFactory.initModel(webRequest, mavContainer, handleSessionAttrMethod);
|
||||
|
||||
assertEquals("sessionAttrValue", mavContainer.getAttribute("sessionAttr"));
|
||||
assertEquals("sessionAttrValue", mavContainer.getModel().get("sessionAttr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateModelBindingResultKeys() throws Exception {
|
||||
String attrName = "attr1";
|
||||
Object attrValue = new Object();
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
mavContainer.addAttribute(attrName, attrValue);
|
||||
|
||||
WebDataBinder dataBinder = new WebDataBinder(attrValue, attrName);
|
||||
@@ -159,8 +160,8 @@ public class ModelFactoryTests {
|
||||
expect(binderFactory.createBinder(webRequest, attrValue, attrName)).andReturn(dataBinder);
|
||||
replay(binderFactory);
|
||||
|
||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler, flashAttrsHandler);
|
||||
modelFactory.updateModel(webRequest, mavContainer, new SimpleSessionStatus(), new SimpleFlashStatus());
|
||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler);
|
||||
modelFactory.updateModel(webRequest, mavContainer, new SimpleSessionStatus());
|
||||
|
||||
assertEquals(attrValue, mavContainer.getModel().remove(attrName));
|
||||
assertSame(dataBinder.getBindingResult(), mavContainer.getModel().remove(bindingResultKey(attrName)));
|
||||
@@ -174,6 +175,7 @@ public class ModelFactoryTests {
|
||||
String attrName = "sessionAttr";
|
||||
String attrValue = "sessionAttrValue";
|
||||
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
mavContainer.addAttribute(attrName, attrValue);
|
||||
sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
|
||||
|
||||
@@ -185,15 +187,13 @@ public class ModelFactoryTests {
|
||||
expect(binderFactory.createBinder(webRequest, attrValue, attrName)).andReturn(dataBinder);
|
||||
replay(binderFactory);
|
||||
|
||||
SimpleSessionStatus status = new SimpleSessionStatus();
|
||||
status.setComplete();
|
||||
SessionStatus sessionStatus = new SimpleSessionStatus();
|
||||
sessionStatus.setComplete();
|
||||
|
||||
// TODO: test with active FlashStatus
|
||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler);
|
||||
modelFactory.updateModel(webRequest, mavContainer, sessionStatus);
|
||||
|
||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler, flashAttrsHandler);
|
||||
modelFactory.updateModel(webRequest, mavContainer, status, new SimpleFlashStatus());
|
||||
|
||||
assertEquals(attrValue, mavContainer.getAttribute(attrName));
|
||||
assertEquals(attrValue, mavContainer.getModel().get(attrName));
|
||||
assertNull(sessionAttributeStore.retrieveAttribute(webRequest, attrName));
|
||||
|
||||
verify(binderFactory);
|
||||
@@ -214,7 +214,7 @@ public class ModelFactoryTests {
|
||||
handlerMethod.setDataBinderFactory(null);
|
||||
handlerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
||||
|
||||
return new ModelFactory(Arrays.asList(handlerMethod), null, sessionAttrsHandler, flashAttrsHandler);
|
||||
return new ModelFactory(Arrays.asList(handlerMethod), null, sessionAttrsHandler);
|
||||
}
|
||||
|
||||
@SessionAttributes("sessionAttr") @SuppressWarnings("unused")
|
||||
|
||||
Reference in New Issue
Block a user