SPR-6464 Add 'setAlwaysUseRedirectAttributes' flag.
When set to 'true' the flag makes RedirectAttributes the only way to add attributes for a redirect thus ignoring the content of the default model even if RedirectAttributes is not in the list of controller method args.
This commit is contained in:
parent
a456a1a0e3
commit
2799e710bc
|
|
@ -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<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache =
|
||||
new ConcurrentHashMap<Class<?>, 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.
|
||||
* <p>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.
|
||||
* <p>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<String, ?> 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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -66,13 +66,14 @@ public class RedirectAttributesModelMap extends ModelMap implements RedirectAttr
|
|||
* <p>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}
|
||||
* <p>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}
|
||||
* <p>Each value is formatted as a String before being added.
|
||||
*/
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends Object> 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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
* <p>For higher-level adapter tests see:
|
||||
* <ul>
|
||||
* <li>{@link ServletAnnotationControllerHandlerMethodTests}
|
||||
* <li>{@link HandlerMethodAnnotationDetectionTests}
|
||||
* <li>{@link RequestMappingHandlerAdapterIntegrationTests}
|
||||
* </ul>
|
||||
* 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<HandlerMethodArgumentResolver> expected = new ArrayList<HandlerMethodArgumentResolver>();
|
||||
expected.add(new ServletRequestMethodArgumentResolver());
|
||||
handlerAdapter.setArgumentResolvers(expected);
|
||||
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<HandlerMethodArgumentResolver>();
|
||||
argumentResolvers.add(new ServletRequestMethodArgumentResolver());
|
||||
|
||||
handlerAdapter.setArgumentResolvers(argumentResolvers);
|
||||
handlerAdapter.afterPropertiesSet();
|
||||
|
||||
HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite)
|
||||
|
|
@ -105,15 +129,16 @@ public class RequestMappingHandlerAdapterTests {
|
|||
List<HandlerMethodArgumentResolver> actual = (List<HandlerMethodArgumentResolver>)
|
||||
new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers");
|
||||
|
||||
assertEquals(expected, actual);
|
||||
assertEquals(argumentResolvers, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setInitBinderArgumentResolvers() {
|
||||
List<HandlerMethodArgumentResolver> expected = new ArrayList<HandlerMethodArgumentResolver>();
|
||||
expected.add(new ServletRequestMethodArgumentResolver());
|
||||
handlerAdapter.setInitBinderArgumentResolvers(expected);
|
||||
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<HandlerMethodArgumentResolver>();
|
||||
argumentResolvers.add(new ServletRequestMethodArgumentResolver());
|
||||
|
||||
handlerAdapter.setInitBinderArgumentResolvers(argumentResolvers);
|
||||
handlerAdapter.afterPropertiesSet();
|
||||
|
||||
HandlerMethodArgumentResolverComposite composite = (HandlerMethodArgumentResolverComposite)
|
||||
|
|
@ -122,15 +147,16 @@ public class RequestMappingHandlerAdapterTests {
|
|||
List<HandlerMethodArgumentResolver> actual = (List<HandlerMethodArgumentResolver>)
|
||||
new DirectFieldAccessor(composite).getPropertyValue("argumentResolvers");
|
||||
|
||||
assertEquals(expected, actual);
|
||||
assertEquals(argumentResolvers, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setReturnValueHandlers() {
|
||||
List<HandlerMethodReturnValueHandler> expected = new ArrayList<HandlerMethodReturnValueHandler>();
|
||||
expected.add(new ModelMethodProcessor());
|
||||
handlerAdapter.setReturnValueHandlers(expected);
|
||||
HandlerMethodReturnValueHandler handler = new ModelMethodProcessor();
|
||||
List<HandlerMethodReturnValueHandler> handlers = Arrays.asList(handler);
|
||||
|
||||
handlerAdapter.setReturnValueHandlers(handlers);
|
||||
handlerAdapter.afterPropertiesSet();
|
||||
|
||||
HandlerMethodReturnValueHandlerComposite composite = (HandlerMethodReturnValueHandlerComposite)
|
||||
|
|
@ -139,14 +165,14 @@ public class RequestMappingHandlerAdapterTests {
|
|||
List<HandlerMethodReturnValueHandler> actual = (List<HandlerMethodReturnValueHandler>)
|
||||
new DirectFieldAccessor(composite).getPropertyValue("returnValueHandlers");
|
||||
|
||||
assertEquals(expected, actual);
|
||||
assertEquals(handlers, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setCustomArgumentResolvers() {
|
||||
TestHanderMethodArgumentResolver resolver = new TestHanderMethodArgumentResolver();
|
||||
handlerAdapter.setCustomArgumentResolvers(Arrays.<HandlerMethodArgumentResolver>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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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<String, Object> map = new HashMap<String, Object>();
|
||||
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<TestBean, String> {
|
||||
|
||||
public String convert(TestBean source) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
* <p>The {@link #setResolveView(boolean)} flag can be used to indicate that
|
||||
* view resolution is not required (e.g. {@code @ResponseBody} method).
|
||||
*
|
||||
* <p>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".
|
||||
* <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.
|
||||
* Whether view resolution is required or not.
|
||||
* <p>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}).
|
||||
* <p>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.
|
||||
* <p>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 void setRedirectModelEnabled() {
|
||||
this.redirectModelEnabled = true;
|
||||
public ModelMap getRedirectModel() {
|
||||
return this.redirectModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the redirect model provided via
|
||||
* {@link #setRedirectModel(ModelMap)} should be used.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue