SPR-6464 Polish following code review.

This commit is contained in:
Rossen Stoyanchev 2011-09-15 18:12:30 +00:00
parent aeba9d244a
commit b2d88ba858
36 changed files with 429 additions and 275 deletions

View File

@ -17,10 +17,10 @@
package org.springframework.web.servlet; package org.springframework.web.servlet;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.beans.BeanUtils; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/** /**
* A FlashMap provides a way for one request to store attributes intended for * A FlashMap provides a way for one request to store attributes intended for
@ -35,7 +35,7 @@ import org.springframework.beans.BeanUtils;
* recipient. On a redirect, the target URL is known and for example * recipient. On a redirect, the target URL is known and for example
* {@code org.springframework.web.servlet.view.RedirectView} has the * {@code org.springframework.web.servlet.view.RedirectView} has the
* opportunity to automatically update the current FlashMap with target * opportunity to automatically update the current FlashMap with target
* URL information . * URL information.
* *
* <p>Annotated controllers will usually not use this type directly. * <p>Annotated controllers will usually not use this type directly.
* See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes} * See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}
@ -46,13 +46,13 @@ import org.springframework.beans.BeanUtils;
* *
* @see FlashMapManager * @see FlashMapManager
*/ */
public class FlashMap extends HashMap<String, Object> implements Comparable<FlashMap> { public final class FlashMap extends HashMap<String, Object> implements Comparable<FlashMap> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private String targetRequestPath; private String targetRequestPath;
private final Map<String, String> targetRequestParams = new LinkedHashMap<String, String>(); private final MultiValueMap<String, String> targetRequestParams = new LinkedMultiValueMap<String, String>();
private long expirationStartTime; private long expirationStartTime;
@ -93,17 +93,15 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
} }
/** /**
* Provide request parameter pairs to identify the request for this FlashMap. * Provide request parameters identifying the request for this FlashMap.
* Only simple type, non-null parameter values are used. * Null or empty keys and values are skipped.
* @param params a Map with the names and values of expected parameters. * @param params a Map with the names and values of expected parameters.
* @see BeanUtils#isSimpleValueType(Class)
*/ */
public FlashMap addTargetRequestParams(Map<String, ?> params) { public FlashMap addTargetRequestParams(MultiValueMap<String, String> params) {
if (params != null) { if (params != null) {
for (String name : params.keySet()) { for (String key : params.keySet()) {
Object value = params.get(name); for (String value : params.get(key)) {
if ((value != null) && BeanUtils.isSimpleValueType(value.getClass())) { addTargetRequestParam(key, value);
this.targetRequestParams.put(name, value.toString());
} }
} }
} }
@ -111,19 +109,21 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
} }
/** /**
* Provide a request parameter to identify the request for this FlashMap. * Provide a request parameter identifying the request for this FlashMap.
* @param name the name of the expected parameter, never {@code null} * @param name the expected parameter name, skipped if {@code null}
* @param value the value for the expected parameter, never {@code null} * @param value the expected parameter value, skipped if {@code null}
*/ */
public FlashMap addTargetRequestParam(String name, String value) { public FlashMap addTargetRequestParam(String name, String value) {
this.targetRequestParams.put(name, value.toString()); if (StringUtils.hasText(name) && StringUtils.hasText(value)) {
this.targetRequestParams.add(name, value);
}
return this; return this;
} }
/** /**
* Return the parameters identifying the target request, or an empty Map. * Return the parameters identifying the target request, or an empty map.
*/ */
public Map<String, String> getTargetRequestParams() { public MultiValueMap<String, String> getTargetRequestParams() {
return targetRequestParams; return targetRequestParams;
} }
@ -174,11 +174,11 @@ public class FlashMap extends HashMap<String, Object> implements Comparable<Flas
@Override @Override
public String toString() { public String toString() {
StringBuilder result = new StringBuilder(); StringBuilder sb = new StringBuilder();
result.append("[Attributes=").append(super.toString()); sb.append("[Attributes=").append(super.toString());
result.append(", targetRequestPath=").append(this.targetRequestPath); sb.append(", targetRequestPath=").append(this.targetRequestPath);
result.append(", targetRequestParams=" + this.targetRequestParams.toString()).append("]"); sb.append(", targetRequestParams=").append(this.targetRequestParams).append("]");
return result.toString(); return sb.toString();
} }
} }

View File

@ -29,8 +29,7 @@ import javax.servlet.http.HttpServletRequest;
* instances are exposed as request attributes and are accessible via methods * instances are exposed as request attributes and are accessible via methods
* in {@code org.springframework.web.servlet.support.RequestContextUtils}. * in {@code org.springframework.web.servlet.support.RequestContextUtils}.
* *
* <p>Annotated controllers are most likely to store and access flash attributes * <p>Annotated controllers will usually not use this FlashMap directly.
* through their model.
* See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}. * See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev

View File

@ -130,6 +130,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
methodAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); methodAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
methodAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef); methodAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
methodAdapterDef.getPropertyValues().add("messageConverters", messageConverters); methodAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
methodAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", true);
if (argumentResolvers != null) { if (argumentResolvers != null) {
methodAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers); methodAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
} }

View File

@ -73,42 +73,59 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
/** /**
* A base class that provides default configuration for Spring MVC applications by registering Spring MVC * A base class that provides default configuration for Spring MVC applications
* infrastructure components to be detected by the {@link DispatcherServlet}. Typically applications should not * by registering Spring MVC infrastructure components to be detected by the
* have to extend this class. A more likely place to start is to annotate an @{@link Configuration} * {@link DispatcherServlet}. An application configuration class is not required
* class with @{@link EnableWebMvc} (see @{@link EnableWebMvc} and {@link WebMvcConfigurer} for details). * to extend this class. A more likely place to start is to annotate
* an @{@link Configuration} class with @{@link EnableWebMvc}
* (see @{@link EnableWebMvc} and {@link WebMvcConfigurer} for details).
* *
* <p>If using @{@link EnableWebMvc} does not give you all you need, consider extending directly from this * <p>If the customization options available with use of @{@link EnableWebMvc}
* class. Remember to add @{@link Configuration} to your subclass and @{@link Bean} to any superclass * are not enough, consider extending directly from this class and override the
* @{@link Bean} methods you choose to override. * appropriate methods. Remember to add @{@link Configuration} to your subclass
* and @{@link Bean} to any superclass @{@link Bean} methods you override.
* *
* <p>This class registers the following {@link HandlerMapping}s:</p> * <p>This class registers the following {@link HandlerMapping}s:</p>
* <ul> * <ul>
* <li>{@link RequestMappingHandlerMapping} ordered at 0 for mapping requests to annotated controller methods. * <li>{@link RequestMappingHandlerMapping}
* <li>{@link HandlerMapping} ordered at 1 to map URL paths directly to view names. * ordered at 0 for mapping requests to annotated controller methods.
* <li>{@link BeanNameUrlHandlerMapping} ordered at 2 to map URL paths to controller bean names. * <li>{@link HandlerMapping}
* <li>{@link HandlerMapping} ordered at {@code Integer.MAX_VALUE-1} to serve static resource requests. * ordered at 1 to map URL paths directly to view names.
* <li>{@link HandlerMapping} ordered at {@code Integer.MAX_VALUE} to forward requests to the default servlet. * <li>{@link BeanNameUrlHandlerMapping}
* ordered at 2 to map URL paths to controller bean names.
* <li>{@link HandlerMapping}
* ordered at {@code Integer.MAX_VALUE-1} to serve static resource requests.
* <li>{@link HandlerMapping}
* ordered at {@code Integer.MAX_VALUE} to forward requests to the default servlet.
* </ul> * </ul>
* *
* <p>Registers these {@link HandlerAdapter}s: * <p>Registers these {@link HandlerAdapter}s:
* <ul> * <ul>
* <li>{@link RequestMappingHandlerAdapter} for processing requests with annotated controller methods. * <li>{@link RequestMappingHandlerAdapter}
* <li>{@link HttpRequestHandlerAdapter} for processing requests with {@link HttpRequestHandler}s. * for processing requests with annotated controller methods.
* <li>{@link SimpleControllerHandlerAdapter} for processing requests with interface-based {@link Controller}s. * <li>{@link HttpRequestHandlerAdapter}
* for processing requests with {@link HttpRequestHandler}s.
* <li>{@link SimpleControllerHandlerAdapter}
* for processing requests with interface-based {@link Controller}s.
* </ul> * </ul>
* *
* <p>Registers a {@link HandlerExceptionResolverComposite} with this chain of exception resolvers: * <p>Registers a {@link HandlerExceptionResolverComposite} with this chain of
* exception resolvers:
* <ul> * <ul>
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through @{@link ExceptionHandler} methods. * <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with @{@link ResponseStatus}. * through @{@link ExceptionHandler} methods.
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types * <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
* with @{@link ResponseStatus}.
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
* exception types
* </ul> * </ul>
* *
* <p>Registers these other instances: * <p>Registers these other instances:
* <ul> * <ul>
* <li>{@link FormattingConversionService} for use with annotated controller methods and the spring:eval JSP tag. * <li>{@link FormattingConversionService}
* <li>{@link Validator} for validating model attributes on annotated controller methods. * for use with annotated controller methods and the spring:eval JSP tag.
* <li>{@link Validator}
* for validating model attributes on annotated controller methods.
* </ul> * </ul>
* *
* @see EnableWebMvc * @see EnableWebMvc
@ -137,7 +154,8 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns a {@link RequestMappingHandlerMapping} ordered at 0 for mapping requests to annotated controllers. * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
* requests to annotated controllers.
*/ */
@Bean @Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() { public RequestMappingHandlerMapping requestMappingHandlerMapping() {
@ -148,8 +166,9 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Provides access to the shared handler interceptors used to configure {@link HandlerMapping} instances with. * Provide access to the shared handler interceptors used to configure
* This method cannot be overridden, use {@link #addInterceptors(InterceptorRegistry)} instead. * {@link HandlerMapping} instances with. This method cannot be overridden,
* use {@link #addInterceptors(InterceptorRegistry)} instead.
*/ */
protected final Object[] getInterceptors() { protected final Object[] getInterceptors() {
if (interceptors == null) { if (interceptors == null) {
@ -162,15 +181,17 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Override this method to add Spring MVC interceptors for pre/post-processing of controller invocation. * Override this method to add Spring MVC interceptors for
* pre- and post-processing of controller invocation.
* @see InterceptorRegistry * @see InterceptorRegistry
*/ */
protected void addInterceptors(InterceptorRegistry registry) { protected void addInterceptors(InterceptorRegistry registry) {
} }
/** /**
* Returns a handler mapping ordered at 1 to map URL paths directly to view names. * Return a handler mapping ordered at 1 to map URL paths directly to
* To configure view controllers, override {@link #addViewControllers(ViewControllerRegistry)}. * view names. To configure view controllers, override
* {@link #addViewControllers}.
*/ */
@Bean @Bean
public HandlerMapping viewControllerHandlerMapping() { public HandlerMapping viewControllerHandlerMapping() {
@ -191,7 +212,8 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL paths to controller bean names. * Return a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL
* paths to controller bean names.
*/ */
@Bean @Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping() { public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
@ -202,8 +224,9 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns a handler mapping ordered at Integer.MAX_VALUE-1 with mapped resource handlers. * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
* To configure resource handling, override {@link #addResourceHandlers(ResourceHandlerRegistry)}. * resource handlers. To configure resource handling, override
* {@link #addResourceHandlers}.
*/ */
@Bean @Bean
public HandlerMapping resourceHandlerMapping() { public HandlerMapping resourceHandlerMapping() {
@ -222,9 +245,9 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns a handler mapping ordered at Integer.MAX_VALUE with a mapped default servlet handler. * Return a handler mapping ordered at Integer.MAX_VALUE with a mapped
* To configure "default" Servlet handling, override * default servlet handler. To configure "default" Servlet handling,
* {@link #configureDefaultServletHandling(DefaultServletHandlerConfigurer)}. * override {@link #configureDefaultServletHandling}.
*/ */
@Bean @Bean
public HandlerMapping defaultServletHandlerMapping() { public HandlerMapping defaultServletHandlerMapping() {
@ -243,12 +266,13 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns a {@link RequestMappingHandlerAdapter} for processing requests through annotated controller methods. * Returns a {@link RequestMappingHandlerAdapter} for processing requests
* Consider overriding one of these other more fine-grained methods: * through annotated controller methods. Consider overriding one of these
* other more fine-grained methods:
* <ul> * <ul>
* <li>{@link #addArgumentResolvers(List)} for adding custom argument resolvers. * <li>{@link #addArgumentResolvers} for adding custom argument resolvers.
* <li>{@link #addReturnValueHandlers(List)} for adding custom return value handlers. * <li>{@link #addReturnValueHandlers} for adding custom return value handlers.
* <li>{@link #configureMessageConverters(List)} for adding custom message converters. * <li>{@link #configureMessageConverters} for adding custom message converters.
* </ul> * </ul>
*/ */
@Bean @Bean
@ -268,34 +292,46 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
adapter.setWebBindingInitializer(webBindingInitializer); adapter.setWebBindingInitializer(webBindingInitializer);
adapter.setCustomArgumentResolvers(argumentResolvers); adapter.setCustomArgumentResolvers(argumentResolvers);
adapter.setCustomReturnValueHandlers(returnValueHandlers); adapter.setCustomReturnValueHandlers(returnValueHandlers);
adapter.setIgnoreDefaultModelOnRedirect(true);
return adapter; return adapter;
} }
/** /**
* Add custom {@link HandlerMethodArgumentResolver}s to use in addition to the ones registered by default. * Add custom {@link HandlerMethodArgumentResolver}s to use in addition to
* <p>Custom argument resolvers are invoked before built-in resolvers except for those that rely on the presence * the ones registered by default.
* of annotations (e.g. {@code @RequestParameter}, {@code @PathVariable}, etc.). The latter can be customized * <p>Custom argument resolvers are invoked before built-in resolvers
* by configuring the {@link RequestMappingHandlerAdapter} directly. * except for those that rely on the presence of annotations (e.g.
* @param argumentResolvers the list of custom converters; initially an empty list. * {@code @RequestParameter}, {@code @PathVariable}, etc.).
* The latter can be customized by configuring the
* {@link RequestMappingHandlerAdapter} directly.
* @param argumentResolvers the list of custom converters;
* initially an empty list.
*/ */
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
} }
/** /**
* Add custom {@link HandlerMethodReturnValueHandler}s in addition to the ones registered by default. * Add custom {@link HandlerMethodReturnValueHandler}s in addition to the
* <p>Custom return value handlers are invoked before built-in ones except for those that rely on the presence * ones registered by default.
* of annotations (e.g. {@code @ResponseBody}, {@code @ModelAttribute}, etc.). The latter can be customized * <p>Custom return value handlers are invoked before built-in ones except
* by configuring the {@link RequestMappingHandlerAdapter} directly. * for those that rely on the presence of annotations (e.g.
* @param returnValueHandlers the list of custom handlers; initially an empty list. * {@code @ResponseBody}, {@code @ModelAttribute}, etc.).
* The latter can be customized by configuring the
* {@link RequestMappingHandlerAdapter} directly.
* @param returnValueHandlers the list of custom handlers;
* initially an empty list.
*/ */
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
} }
/** /**
* Provides access to the shared {@link HttpMessageConverter}s used by the * Provides access to the shared {@link HttpMessageConverter}s used by the
* {@link RequestMappingHandlerAdapter} and the {@link ExceptionHandlerExceptionResolver}. * {@link RequestMappingHandlerAdapter} and the
* This method cannot be overridden. Use {@link #configureMessageConverters(List)} instead. * {@link ExceptionHandlerExceptionResolver}.
* Also see {@link #addDefaultHttpMessageConverters(List)} that can be used to add default message converters. * This method cannot be overridden.
* Use {@link #configureMessageConverters(List)} instead.
* Also see {@link #addDefaultHttpMessageConverters(List)} that can be
* used to add default message converters.
*/ */
protected final List<HttpMessageConverter<?>> getMessageConverters() { protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (messageConverters == null) { if (messageConverters == null) {
@ -309,17 +345,20 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Override this method to add custom {@link HttpMessageConverter}s to use with * Override this method to add custom {@link HttpMessageConverter}s to use
* the {@link RequestMappingHandlerAdapter} and the {@link ExceptionHandlerExceptionResolver}. * with the {@link RequestMappingHandlerAdapter} and the
* Adding converters to the list turns off the default converters that would otherwise be registered by default. * {@link ExceptionHandlerExceptionResolver}. Adding converters to the
* Also see {@link #addDefaultHttpMessageConverters(List)} that can be used to add default message converters. * list turns off the default converters that would otherwise be registered
* @param converters a list to add message converters to; initially an empty list. * by default. Also see {@link #addDefaultHttpMessageConverters(List)} that
* can be used to add default message converters.
* @param converters a list to add message converters to;
* initially an empty list.
*/ */
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
} }
/** /**
* A method available to subclasses to add default {@link HttpMessageConverter}s. * Override this method to add default {@link HttpMessageConverter}s.
* @param messageConverters the list to add the default message converters to * @param messageConverters the list to add the default message converters to
*/ */
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) { protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
@ -346,9 +385,9 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns a {@link FormattingConversionService} for use with annotated controller methods and the * Returns a {@link FormattingConversionService} for use with annotated
* {@code spring:eval} JSP tag. Also see {@link #addFormatters(FormatterRegistry)} as an alternative * controller methods and the {@code spring:eval} JSP tag.
* to overriding this method. * Also see {@link #addFormatters} as an alternative to overriding this method.
*/ */
@Bean @Bean
public FormattingConversionService mvcConversionService() { public FormattingConversionService mvcConversionService() {
@ -364,8 +403,9 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns {@link Validator} for validating {@code @ModelAttribute} and {@code @RequestBody} arguments of * Returns {@link Validator} for validating {@code @ModelAttribute}
* annotated controller methods. To configure a custom validation, override {@link #getValidator()}. * and {@code @RequestBody} arguments of annotated controller methods.
* To configure a custom validation, override {@link #getValidator()}.
*/ */
@Bean @Bean
Validator mvcValidator() { Validator mvcValidator() {
@ -404,7 +444,8 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns a {@link HttpRequestHandlerAdapter} for processing requests with {@link HttpRequestHandler}s. * Returns a {@link HttpRequestHandlerAdapter} for processing requests
* with {@link HttpRequestHandler}s.
*/ */
@Bean @Bean
public HttpRequestHandlerAdapter httpRequestHandlerAdapter() { public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
@ -412,7 +453,8 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns a {@link SimpleControllerHandlerAdapter} for processing requests with interface-based controllers. * Returns a {@link SimpleControllerHandlerAdapter} for processing requests
* with interface-based controllers.
*/ */
@Bean @Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
@ -420,8 +462,9 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Returns a {@link HandlerExceptionResolverComposite} that contains a list of exception resolvers. * Returns a {@link HandlerExceptionResolverComposite} that contains a list
* To customize the list of exception resolvers, override {@link #configureHandlerExceptionResolvers(List)}. * of exception resolvers. To customize the list of exception resolvers,
* override {@link #configureHandlerExceptionResolvers(List)}.
*/ */
@Bean @Bean
HandlerExceptionResolver handlerExceptionResolver() throws Exception { HandlerExceptionResolver handlerExceptionResolver() throws Exception {
@ -439,21 +482,28 @@ public abstract class WebMvcConfigurationSupport implements ApplicationContextAw
} }
/** /**
* Override this method to configure the list of {@link HandlerExceptionResolver}s to use. * Override this method to configure the list of
* Adding resolvers to the list turns off the default resolvers that would otherwise be registered by default. * {@link HandlerExceptionResolver}s to use. Adding resolvers to the list
* Also see {@link #addDefaultHandlerExceptionResolvers(List)} that can be used to add the default exception resolvers. * turns off the default resolvers that would otherwise be registered by
* @param exceptionResolvers a list to add exception resolvers to; initially an empty list. * default. Also see {@link #addDefaultHandlerExceptionResolvers(List)}
* that can be used to add the default exception resolvers.
* @param exceptionResolvers a list to add exception resolvers to;
* initially an empty list.
*/ */
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
} }
/** /**
* A method available to subclasses for adding default {@link HandlerExceptionResolver}s. * A method available to subclasses for adding default
* {@link HandlerExceptionResolver}s.
* <p>Adds the following exception resolvers: * <p>Adds the following exception resolvers:
* <ul> * <ul>
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through @{@link ExceptionHandler} methods. * <li>{@link ExceptionHandlerExceptionResolver}
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with @{@link ResponseStatus}. * for handling exceptions through @{@link ExceptionHandler} methods.
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types * <li>{@link ResponseStatusExceptionResolver}
* for exceptions annotated with @{@link ResponseStatus}.
* <li>{@link DefaultHandlerExceptionResolver}
* for resolving known Spring exception types
* </ul> * </ul>
*/ */
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {

View File

@ -235,7 +235,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
return null; return null;
} }
if (!mavContainer.isResolveView()) { if (mavContainer.isRequestHandled()) {
return new ModelAndView(); return new ModelAndView();
} }
else { else {

View File

@ -93,7 +93,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.ServletResponseMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler; 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.RedirectAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
@ -147,7 +146,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore(); private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
private boolean alwaysUseRedirectAttributes; private boolean ignoreDefaultModelOnRedirect = false;
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache =
new ConcurrentHashMap<Class<?>, SessionAttributesHandler>(); new ConcurrentHashMap<Class<?>, SessionAttributesHandler>();
@ -334,19 +333,20 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
} }
/** /**
* By default a controller uses {@link Model} to select attributes for * A controller can use the "default" {@link Model} in rendering and
* rendering and for redirecting. However, a controller can also use * redirect scenarios. Alternatively, it can use {@link RedirectAttributes}
* {@link RedirectAttributes} to select attributes before a redirect. * to provide attributes for a redirect scenario.
* <p>When this flag is set to {@code true}, {@link RedirectAttributes} * <p>When this flag is set to {@code true}, the "default" model is never
* becomes the only way to select attributes for a redirect. * used in a redirect even if the controller method doesn't explicitly
* In other words, for a redirect a controller must use * declare a RedirectAttributes argument.
* {@link RedirectAttributes} or no attributes will be used. * <p>When set to {@code false}, the "default" model may be used in a
* <p>The default value is {@code false}, meaning the {@link Model} is * redirect if the controller method doesn't explicitly declare a
* used unless {@link RedirectAttributes} is used. * RedirectAttributes argument.
* <p>The default setting is {@code false}.
* @see RedirectAttributes * @see RedirectAttributes
*/ */
public void setAlwaysUseRedirectAttributes(boolean alwaysUseRedirectAttributes) { public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {
this.alwaysUseRedirectAttributes = alwaysUseRedirectAttributes; this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;
} }
public void setBeanFactory(BeanFactory beanFactory) { public void setBeanFactory(BeanFactory beanFactory) {
@ -538,18 +538,14 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
ModelAndViewContainer mavContainer = new ModelAndViewContainer(); ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod); modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
if (this.alwaysUseRedirectAttributes) {
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
mavContainer.setRedirectModel(new RedirectAttributesModelMap(dataBinder));
}
SessionStatus sessionStatus = new SimpleSessionStatus(); SessionStatus sessionStatus = new SimpleSessionStatus();
requestMappingMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus); requestMappingMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus);
modelFactory.updateModel(webRequest, mavContainer, sessionStatus); modelFactory.updateModel(webRequest, mavContainer, sessionStatus);
if (!mavContainer.isResolveView()) { if (mavContainer.isRequestHandled()) {
return null; return null;
} }
else { else {

View File

@ -75,7 +75,7 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
* <p>Return value handling may be skipped entirely when the method returns {@code null} (also possibly due * <p>Return value handling may be skipped entirely when the method returns {@code null} (also possibly due
* to a {@code void} return type) and one of the following additional conditions is true: * to a {@code void} return type) and one of the following additional conditions is true:
* <ul> * <ul>
* <li>A {@link HandlerMethodArgumentResolver} has set the {@link ModelAndViewContainer#setResolveView(boolean)} * <li>A {@link HandlerMethodArgumentResolver} has set the {@link ModelAndViewContainer#setRequestHandled(boolean)}
* flag to {@code false} -- e.g. method arguments providing access to the response. * flag to {@code false} -- e.g. method arguments providing access to the response.
* <li>The request qualifies as "not modified" as defined in {@link ServletWebRequest#checkNotModified(long)} * <li>The request qualifies as "not modified" as defined in {@link ServletWebRequest#checkNotModified(long)}
* and {@link ServletWebRequest#checkNotModified(String)}. In this case a response with "not modified" response * and {@link ServletWebRequest#checkNotModified(String)}. In this case a response with "not modified" response
@ -98,13 +98,13 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
setResponseStatus((ServletWebRequest) request); setResponseStatus((ServletWebRequest) request);
if (returnValue == null) { if (returnValue == null) {
if (isRequestNotModified(request) || hasResponseStatus() || !mavContainer.isResolveView()) { if (isRequestNotModified(request) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setResolveView(false); mavContainer.setRequestHandled(true);
return; return;
} }
} }
mavContainer.setResolveView(true); mavContainer.setRequestHandled(false);
try { try {
returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request); returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request);

View File

@ -101,7 +101,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception { NativeWebRequest webRequest) throws Exception {
mavContainer.setResolveView(false); mavContainer.setRequestHandled(true);
if (returnValue == null) { if (returnValue == null) {
return; return;

View File

@ -24,7 +24,7 @@ import org.springframework.web.servlet.ModelAndView;
/** /**
* Handles return values of type {@link ModelAndView} transferring their content to the {@link ModelAndViewContainer}. * Handles return values of type {@link ModelAndView} transferring their content to the {@link ModelAndViewContainer}.
* If the return value is {@code null}, the {@link ModelAndViewContainer#setResolveView(boolean)} flag is set to * If the return value is {@code null}, the {@link ModelAndViewContainer#setRequestHandled(boolean)} flag is set to
* {@code false} to indicate view resolution is not needed. * {@code false} to indicate view resolution is not needed.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -49,7 +49,7 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn
mavContainer.addAllAttributes(mav.getModel()); mavContainer.addAllAttributes(mav.getModel());
} }
else { else {
mavContainer.setResolveView(false); mavContainer.setRequestHandled(true);
} }
} }

View File

@ -49,16 +49,10 @@ public class RedirectAttributesMethodArgumentResolver implements HandlerMethodAr
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception { WebDataBinderFactory binderFactory) throws Exception {
if (mavContainer.getRedirectModel() != null) {
return mavContainer.getRedirectModel();
}
else {
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null); DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder); ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder);
mavContainer.setRedirectModel(redirectAttributes); mavContainer.setRedirectModel(redirectAttributes);
return redirectAttributes; return redirectAttributes;
} }
}
} }

View File

@ -98,7 +98,7 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
MethodParameter returnType, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException { NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException {
mavContainer.setResolveView(false); mavContainer.setRequestHandled(true);
if (returnValue != null) { if (returnValue != null) {
writeWithMessageConverters(returnValue, returnType, webRequest); writeWithMessageConverters(returnValue, returnType, webRequest);
} }

View File

@ -54,7 +54,7 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
/** /**
* {@inheritDoc} * {@inheritDoc}
* <p>Sets the {@link ModelAndViewContainer#setResolveView(boolean)} flag to {@code false} to indicate * <p>Sets the {@link ModelAndViewContainer#setRequestHandled(boolean)} flag to {@code false} to indicate
* that the method signature provides access to the response. If subsequently the underlying method * that the method signature provides access to the response. If subsequently the underlying method
* returns {@code null}, view resolution will be bypassed. * returns {@code null}, view resolution will be bypassed.
* @see ServletInvocableHandlerMethod#invokeAndHandle(NativeWebRequest, ModelAndViewContainer, Object...) * @see ServletInvocableHandlerMethod#invokeAndHandle(NativeWebRequest, ModelAndViewContainer, Object...)
@ -64,7 +64,7 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
NativeWebRequest webRequest, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws IOException { WebDataBinderFactory binderFactory) throws IOException {
mavContainer.setResolveView(false); mavContainer.setRequestHandled(true);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Class<?> paramType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();

View File

@ -61,14 +61,14 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan
String viewName = (String) returnValue; String viewName = (String) returnValue;
mavContainer.setViewName(viewName); mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) { if (isRedirectViewName(viewName)) {
mavContainer.setUseRedirectModel(); mavContainer.setUseRedirectModel(true);
} }
} }
else if (returnValue instanceof View){ else if (returnValue instanceof View){
View view = (View) returnValue; View view = (View) returnValue;
mavContainer.setView(view); mavContainer.setView(view);
if (isRedirectView(view)) { if (isRedirectView(view)) {
mavContainer.setUseRedirectModel(); mavContainer.setUseRedirectModel(true);
} }
} }
else { else {
@ -95,7 +95,7 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan
* "false" otherwise. * "false" otherwise.
*/ */
protected boolean isRedirectView(View view) { protected boolean isRedirectView(View view) {
if (SmartView.class.isAssignableFrom(view.getClass())) { if (view instanceof SmartView) {
return ((SmartView) view).isRedirectView(); return ((SmartView) view).isRedirectView();
} }
else { else {

View File

@ -27,13 +27,14 @@ import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.FlashMapManager; import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
/** /**
* A default {@link FlashMapManager} implementation keeps {@link FlashMap} * A default {@link FlashMapManager} implementation that stores {@link FlashMap}
* instances in the HTTP session. * instances in the HTTP session.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -122,9 +123,10 @@ public class DefaultFlashMapManager implements FlashMapManager {
return false; return false;
} }
} }
if (flashMap.getTargetRequestParams() != null) { MultiValueMap<String, String> params = flashMap.getTargetRequestParams();
for (String paramName : flashMap.getTargetRequestParams().keySet()) { for (String key : params.keySet()) {
if (!flashMap.getTargetRequestParams().get(paramName).equals(request.getParameter(paramName))) { for (String value : params.get(key)) {
if (!value.equals(request.getParameter(key))) {
return false; return false;
} }
} }

View File

@ -43,6 +43,8 @@ import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.SmartView; import org.springframework.web.servlet.SmartView;
import org.springframework.web.servlet.View; import org.springframework.web.servlet.View;
import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils; import org.springframework.web.util.UriUtils;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
@ -240,11 +242,9 @@ public class RedirectView extends AbstractUrlBasedView implements SmartView {
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
if (!CollectionUtils.isEmpty(flashMap)) { if (!CollectionUtils.isEmpty(flashMap)) {
String targetPath = WebUtils.extractUrlPath(targetUrl.toString()); UriComponents uriComponents = UriComponentsBuilder.fromUriString(targetUrl).build();
flashMap.setTargetRequestPath(targetPath); flashMap.setTargetRequestPath(uriComponents.getPath());
if (this.exposeModelAttributes) { flashMap.addTargetRequestParams(uriComponents.getQueryParams());
flashMap.addTargetRequestParams(model);
}
} }
sendRedirect(request, response, targetUrl.toString(), this.http10Compatible); sendRedirect(request, response, targetUrl.toString(), this.http10Compatible);

View File

@ -21,6 +21,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.junit.Test; import org.junit.Test;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/** /**
* Test fixture for {@link FlashMap} tests. * Test fixture for {@link FlashMap} tests.
@ -31,18 +33,17 @@ public class FlashMapTests {
@Test @Test
public void isExpired() throws InterruptedException { public void isExpired() throws InterruptedException {
assertFalse(new FlashMap().isExpired());
FlashMap flashMap = new FlashMap(); FlashMap flashMap = new FlashMap();
flashMap.startExpirationPeriod(0); flashMap.startExpirationPeriod(0);
Thread.sleep(100);
Thread.sleep(1);
assertTrue(flashMap.isExpired()); assertTrue(flashMap.isExpired());
} }
@Test @Test
public void notExpired() throws InterruptedException { public void notExpired() throws InterruptedException {
assertFalse(new FlashMap().isExpired());
FlashMap flashMap = new FlashMap(); FlashMap flashMap = new FlashMap();
flashMap.startExpirationPeriod(10); flashMap.startExpirationPeriod(10);
Thread.sleep(100); Thread.sleep(100);
@ -71,4 +72,51 @@ public class FlashMapTests {
assertEquals(0, flashMap1.compareTo(flashMap2)); assertEquals(0, flashMap1.compareTo(flashMap2));
} }
@Test
public void addTargetRequestParamNullValue() {
FlashMap flashMap = new FlashMap();
flashMap.addTargetRequestParam("text", "abc");
flashMap.addTargetRequestParam("empty", " ");
flashMap.addTargetRequestParam("null", null);
assertEquals(1, flashMap.getTargetRequestParams().size());
assertEquals("abc", flashMap.getTargetRequestParams().getFirst("text"));
}
@Test
public void addTargetRequestParamsNullValue() {
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("key", "abc");
params.add("key", " ");
params.add("key", null);
FlashMap flashMap = new FlashMap();
flashMap.addTargetRequestParams(params);
assertEquals(1, flashMap.getTargetRequestParams().size());
assertEquals(1, flashMap.getTargetRequestParams().get("key").size());
assertEquals("abc", flashMap.getTargetRequestParams().getFirst("key"));
}
@Test
public void addTargetRequestParamNullKey() {
FlashMap flashMap = new FlashMap();
flashMap.addTargetRequestParam(" ", "abc");
flashMap.addTargetRequestParam(null, "abc");
assertTrue(flashMap.getTargetRequestParams().isEmpty());
}
@Test
public void addTargetRequestParamsNullKey() {
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add(" ", "abc");
params.add(null, " ");
FlashMap flashMap = new FlashMap();
flashMap.addTargetRequestParams(params);
assertTrue(flashMap.getTargetRequestParams().isEmpty());
}
} }

View File

@ -67,6 +67,7 @@ public class AnnotationDrivenBeanDefinitionParserTests {
MessageCodesResolver resolver = ((ConfigurableWebBindingInitializer) initializer).getMessageCodesResolver(); MessageCodesResolver resolver = ((ConfigurableWebBindingInitializer) initializer).getMessageCodesResolver();
assertNotNull(resolver); assertNotNull(resolver);
assertEquals(TestMessageCodesResolver.class, resolver.getClass()); assertEquals(TestMessageCodesResolver.class, resolver.getClass());
assertEquals(true, new DirectFieldAccessor(adapter).getPropertyValue("ignoreDefaultModelOnRedirect"));
} }
@Test @Test

View File

@ -150,6 +150,8 @@ public class WebMvcConfigurationSupportTests {
Validator validator = initializer.getValidator(); Validator validator = initializer.getValidator();
assertNotNull(validator); assertNotNull(validator);
assertTrue(validator instanceof LocalValidatorFactoryBean); assertTrue(validator instanceof LocalValidatorFactoryBean);
assertEquals(true, new DirectFieldAccessor(adapter).getPropertyValue("ignoreDefaultModelOnRedirect"));
} }
@Test @Test

View File

@ -103,7 +103,7 @@ public class RequestMappingHandlerAdapterTests {
handlerAdapter.setArgumentResolvers(Arrays.asList(redirectAttributesResolver, modelResolver)); handlerAdapter.setArgumentResolvers(Arrays.asList(redirectAttributesResolver, modelResolver));
handlerAdapter.setReturnValueHandlers(Arrays.asList(viewHandler)); handlerAdapter.setReturnValueHandlers(Arrays.asList(viewHandler));
handlerAdapter.setAlwaysUseRedirectAttributes(true); handlerAdapter.setIgnoreDefaultModelOnRedirect(true);
handlerAdapter.afterPropertiesSet(); handlerAdapter.afterPropertiesSet();
request.setAttribute(FlashMapManager.OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FlashMapManager.OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
@ -111,7 +111,7 @@ public class RequestMappingHandlerAdapterTests {
HandlerMethod handlerMethod = handlerMethod(new RedirectAttributeHandler(), "handle", Model.class); HandlerMethod handlerMethod = handlerMethod(new RedirectAttributeHandler(), "handle", Model.class);
ModelAndView mav = handlerAdapter.handle(request, response, handlerMethod); ModelAndView mav = handlerAdapter.handle(request, response, handlerMethod);
assertTrue("No redirect attributes added, model should be empty", mav.getModel().isEmpty()); assertTrue("Without RedirectAttributes arg, model should be empty", mav.getModel().isEmpty());
} }
@Test @Test

View File

@ -71,8 +71,8 @@ public class ServletInvocableHandlerMethodTests {
ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("responseStatus"); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("responseStatus");
handlerMethod.invokeAndHandle(webRequest, mavContainer); handlerMethod.invokeAndHandle(webRequest, mavContainer);
assertFalse("Null return value + @ResponseStatus should result in 'no view resolution'", assertTrue("Null return value + @ResponseStatus should result in 'request handled'",
mavContainer.isResolveView()); mavContainer.isRequestHandled());
assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus()); assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
assertEquals("400 Bad Request", response.getErrorMessage()); assertEquals("400 Bad Request", response.getErrorMessage());
@ -85,8 +85,8 @@ public class ServletInvocableHandlerMethodTests {
ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("httpServletResponse", HttpServletResponse.class); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("httpServletResponse", HttpServletResponse.class);
handlerMethod.invokeAndHandle(webRequest, mavContainer); handlerMethod.invokeAndHandle(webRequest, mavContainer);
assertFalse("Null return value + HttpServletResponse arg should result in 'no view resolution'", assertTrue("Null return value + HttpServletResponse arg should result in 'request handled'",
mavContainer.isResolveView()); mavContainer.isRequestHandled());
} }
@Test @Test
@ -98,8 +98,8 @@ public class ServletInvocableHandlerMethodTests {
ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("notModified"); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("notModified");
handlerMethod.invokeAndHandle(webRequest, mavContainer); handlerMethod.invokeAndHandle(webRequest, mavContainer);
assertFalse("Null return value + 'not modified' request should result in 'no view resolution'", assertTrue("Null return value + 'not modified' request should result in 'request handled'",
mavContainer.isResolveView()); mavContainer.isRequestHandled());
} }
@Test @Test

View File

@ -17,6 +17,7 @@
package org.springframework.web.servlet.mvc.method.annotation.support; package org.springframework.web.servlet.mvc.method.annotation.support;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -70,7 +71,7 @@ public class DefaultMethodReturnValueHandlerTests {
assertEquals("viewName", mavContainer.getViewName()); assertEquals("viewName", mavContainer.getViewName());
assertSame(testBean, mavContainer.getModel().get("modelAttrName")); assertSame(testBean, mavContainer.getModel().get("modelAttrName"));
assertTrue(mavContainer.isResolveView()); assertFalse(mavContainer.isRequestHandled());
} }
@Test(expected=UnsupportedOperationException.class) @Test(expected=UnsupportedOperationException.class)

View File

@ -138,7 +138,7 @@ public class HttpEntityMethodProcessorTests {
Object result = processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null); Object result = processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
assertTrue(result instanceof HttpEntity); assertTrue(result instanceof HttpEntity);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView()); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
assertEquals("Invalid argument", body, ((HttpEntity<?>) result).getBody()); assertEquals("Invalid argument", body, ((HttpEntity<?>) result).getBody());
verify(messageConverter); verify(messageConverter);
} }
@ -179,7 +179,7 @@ public class HttpEntityMethodProcessorTests {
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView()); assertTrue(mavContainer.isRequestHandled());
verify(messageConverter); verify(messageConverter);
} }
@ -197,7 +197,7 @@ public class HttpEntityMethodProcessorTests {
processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest); processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView()); assertTrue(mavContainer.isRequestHandled());
verify(messageConverter); verify(messageConverter);
} }
@ -245,7 +245,7 @@ public class HttpEntityMethodProcessorTests {
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView()); assertTrue(mavContainer.isRequestHandled());
assertEquals("headerValue", servletResponse.getHeader("headerName")); assertEquals("headerValue", servletResponse.getHeader("headerName"));
} }
@ -264,7 +264,7 @@ public class HttpEntityMethodProcessorTests {
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView()); assertTrue(mavContainer.isRequestHandled());
assertEquals("headerValue", outputMessage.getValue().getHeaders().get("header").get(0)); assertEquals("headerValue", outputMessage.getValue().getHeaders().get("header").get(0));
verify(messageConverter); verify(messageConverter);
} }

View File

@ -79,7 +79,7 @@ public class ModelAndViewMethodReturnValueHandlerTests {
public void handleReturnValueNull() throws Exception { public void handleReturnValueNull() throws Exception {
handler.handleReturnValue(null, getReturnValueParam("modelAndView"), mavContainer, webRequest); handler.handleReturnValue(null, getReturnValueParam("modelAndView"), mavContainer, webRequest);
assertFalse(mavContainer.isResolveView()); assertTrue(mavContainer.isRequestHandled());
} }
private MethodParameter getReturnValueParam(String methodName) throws Exception { private MethodParameter getReturnValueParam(String methodName) throws Exception {

View File

@ -245,7 +245,7 @@ public class RequestPartMethodArgumentResolverTests {
Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory()); Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory());
assertEquals("Invalid argument value", argValue, actualValue); assertEquals("Invalid argument value", argValue, actualValue);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView()); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
verify(messageConverter); verify(messageConverter);
} }

View File

@ -140,7 +140,7 @@ public class RequestResponseBodyMethodProcessorTests {
Object result = processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null); Object result = processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
assertEquals("Invalid argument", body, result); assertEquals("Invalid argument", body, result);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView()); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
verify(messageConverter); verify(messageConverter);
} }
@ -211,7 +211,7 @@ public class RequestResponseBodyMethodProcessorTests {
processor.handleReturnValue(body, returnTypeString, mavContainer, webRequest); processor.handleReturnValue(body, returnTypeString, mavContainer, webRequest);
assertFalse("The ResolveView flag wasn't turned off", mavContainer.isResolveView()); assertTrue("The requestHandled flag wasn't set", mavContainer.isRequestHandled());
verify(messageConverter); verify(messageConverter);
} }
@ -228,7 +228,7 @@ public class RequestResponseBodyMethodProcessorTests {
processor.handleReturnValue(body, returnTypeStringProduces, mavContainer, webRequest); processor.handleReturnValue(body, returnTypeStringProduces, mavContainer, webRequest);
assertFalse(mavContainer.isResolveView()); assertTrue(mavContainer.isRequestHandled());
verify(messageConverter); verify(messageConverter);
} }

View File

@ -75,7 +75,7 @@ public class ServletRequestMethodArgumentResolverTests {
assertTrue("ServletRequest not supported", isSupported); assertTrue("ServletRequest not supported", isSupported);
assertSame("Invalid result", servletRequest, result); assertSame("Invalid result", servletRequest, result);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView()); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
} }
@Test @Test
@ -89,7 +89,7 @@ public class ServletRequestMethodArgumentResolverTests {
assertTrue("Session not supported", isSupported); assertTrue("Session not supported", isSupported);
assertSame("Invalid result", session, result); assertSame("Invalid result", session, result);
assertTrue("The ResolveView flag shouldn't change", mavContainer.isResolveView()); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
} }
@Test @Test

View File

@ -67,7 +67,7 @@ public class ServletResponseMethodArgumentResolverTests {
Object result = resolver.resolveArgument(servletResponseParameter, mavContainer, webRequest, null); Object result = resolver.resolveArgument(servletResponseParameter, mavContainer, webRequest, null);
assertSame("Invalid result", servletResponse, result); assertSame("Invalid result", servletResponse, result);
assertFalse(mavContainer.isResolveView()); assertTrue(mavContainer.isRequestHandled());
} }
@Test @Test
@ -78,7 +78,7 @@ public class ServletResponseMethodArgumentResolverTests {
Object result = resolver.resolveArgument(outputStreamParameter, mavContainer, webRequest, null); Object result = resolver.resolveArgument(outputStreamParameter, mavContainer, webRequest, null);
assertSame("Invalid result", servletResponse.getOutputStream(), result); assertSame("Invalid result", servletResponse.getOutputStream(), result);
assertFalse(mavContainer.isResolveView()); assertTrue(mavContainer.isRequestHandled());
} }
@Test @Test
@ -89,7 +89,7 @@ public class ServletResponseMethodArgumentResolverTests {
Object result = resolver.resolveArgument(writerParameter, mavContainer, webRequest, null); Object result = resolver.resolveArgument(writerParameter, mavContainer, webRequest, null);
assertSame("Invalid result", servletResponse.getWriter(), result); assertSame("Invalid result", servletResponse.getWriter(), result);
assertFalse(mavContainer.isResolveView()); assertTrue(mavContainer.isRequestHandled());
} }
public void supportedParams(ServletResponse p0, OutputStream p1, Writer p2) { public void supportedParams(ServletResponse p0, OutputStream p1, Writer p2) {

View File

@ -163,9 +163,7 @@ public class DefaultFlashMapManagerTests {
allMaps.add(flashMap); allMaps.add(flashMap);
flashMap.startExpirationPeriod(0); flashMap.startExpirationPeriod(0);
} }
Thread.sleep(100);
Thread.sleep(5);
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request);
assertEquals(0, allMaps.size()); assertEquals(0, allMaps.size());
@ -197,7 +195,7 @@ public class DefaultFlashMapManagerTests {
this.flashMapManager.setFlashMapTimeout(0); this.flashMapManager.setFlashMapTimeout(0);
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request);
Thread.sleep(1); Thread.sleep(100);
List<FlashMap> allMaps = getFlashMaps(); List<FlashMap> allMaps = getFlashMaps();

View File

@ -129,7 +129,7 @@ public class RedirectViewTests {
assertEquals("http://url.somewhere.com/path?id=1", response.getHeader("Location")); assertEquals("http://url.somewhere.com/path?id=1", response.getHeader("Location"));
assertEquals("/path", flashMap.getTargetRequestPath()); assertEquals("/path", flashMap.getTargetRequestPath());
assertEquals(model, flashMap.getTargetRequestParams()); assertEquals(model, flashMap.getTargetRequestParams().toSingleValueMap());
} }
@Test @Test

View File

@ -211,7 +211,7 @@ public final class ModelFactory {
this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel()); this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
} }
if (mavContainer.isResolveView()) { if (!mavContainer.isRequestHandled()) {
updateBindingResult(request, mavContainer.getModel()); updateBindingResult(request, mavContainer.getModel());
} }
} }

View File

@ -22,7 +22,8 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
/** /**
* Strategy interface for resolving method parameters into argument values in the context of a given request. * Strategy interface for resolving method parameters into argument values in
* the context of a given request.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @since 3.1 * @since 3.1
@ -30,21 +31,27 @@ import org.springframework.web.context.request.NativeWebRequest;
public interface HandlerMethodArgumentResolver { public interface HandlerMethodArgumentResolver {
/** /**
* Whether the given {@linkplain MethodParameter method parameter} is supported by this resolver. * Whether the given {@linkplain MethodParameter method parameter} is
* supported by this resolver.
* *
* @param parameter the method parameter to check * @param parameter the method parameter to check
* @return {@code true} if this resolver supports the supplied parameter; {@code false} otherwise * @return {@code true} if this resolver supports the supplied parameter;
* {@code false} otherwise
*/ */
boolean supportsParameter(MethodParameter parameter); boolean supportsParameter(MethodParameter parameter);
/** /**
* Resolves a method parameter into an argument value from a given request. A {@link ModelAndViewContainer} * Resolves a method parameter into an argument value from a given request.
* provides access to the model for the request. A {@link WebDataBinderFactory} provides a way to create * A {@link ModelAndViewContainer} provides access to the model for the
* a {@link WebDataBinder} instance when needed for data binding and type conversion purposes. * request. A {@link WebDataBinderFactory} provides a way to create
* a {@link WebDataBinder} instance when needed for data binding and
* type conversion purposes.
* *
* @param parameter the method parameter to resolve. This parameter must have previously been passed to * @param parameter the method parameter to resolve. This parameter must
* {@link #supportsParameter(org.springframework.core.MethodParameter)} and it must have returned {@code true} * have previously been passed to
* @param mavContainer the {@link ModelAndViewContainer} for the current request * {@link #supportsParameter(org.springframework.core.MethodParameter)}
* and it must have returned {@code true}
* @param mavContainer the ModelAndViewContainer for the current request
* @param webRequest the current request * @param webRequest the current request
* @param binderFactory a factory for creating {@link WebDataBinder} instances * @param binderFactory a factory for creating {@link WebDataBinder} instances
* @return the resolved argument value, or {@code null}. * @return the resolved argument value, or {@code null}.

View File

@ -20,7 +20,8 @@ import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
/** /**
* Strategy interface to handle the value returned from the invocation of a handler method . * Strategy interface to handle the value returned from the invocation of a
* handler method .
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @since 3.1 * @since 3.1
@ -28,24 +29,27 @@ import org.springframework.web.context.request.NativeWebRequest;
public interface HandlerMethodReturnValueHandler { public interface HandlerMethodReturnValueHandler {
/** /**
* Whether the given {@linkplain MethodParameter method return type} is supported by this handler. * Whether the given {@linkplain MethodParameter method return type} is
* supported by this handler.
* *
* @param returnType the method return type to check * @param returnType the method return type to check
* @return {@code true} if this handler supports the supplied return type; {@code false} otherwise * @return {@code true} if this handler supports the supplied return type;
* {@code false} otherwise
*/ */
boolean supportsReturnType(MethodParameter returnType); boolean supportsReturnType(MethodParameter returnType);
/** /**
* Handle the given return value by adding attributes to the model, setting the view (or view name), * Handle the given return value by adding attributes to the model and
* or by writing to the response. {@link HandlerMethodReturnValueHandler} implementations should also * setting a view or setting the
* consider whether to set {@link ModelAndViewContainer#setResolveView(boolean)}, which is set to * {@link ModelAndViewContainer#setRequestHandled} flag to {@code true}
* {@code true} by default and therefore needs to be set to {@code false} explicitly if view * to indicate the response has been handled directly.
* resolution is to be bypassed.
* *
* @param returnValue the value returned from the handler method * @param returnValue the value returned from the handler method
* @param returnType the type of the return value. This type must have previously been passed to * @param returnType the type of the return value. This type must have
* {@link #supportsReturnType(org.springframework.core.MethodParameter)} and it must have returned {@code true} * previously been passed to
* @param mavContainer the {@link ModelAndViewContainer} for the current request * {@link #supportsReturnType(org.springframework.core.MethodParameter)}
* and it must have returned {@code true}
* @param mavContainer the ModelAndViewContainer for the current request
* @param webRequest the current request * @param webRequest the current request
* @throws Exception if the return value handling results in an error * @throws Exception if the return value handling results in an error
*/ */

View File

@ -28,14 +28,14 @@ import org.springframework.validation.support.BindingAwareModelMap;
* {@link HandlerMethodReturnValueHandler}s during the course of invocation of * {@link HandlerMethodReturnValueHandler}s during the course of invocation of
* a controller method. * a controller method.
* *
* <p>The {@link #setResolveView(boolean)} flag can be used to indicate that * <p>The {@link #setRequestHandled} flag can be used to indicate the request
* view resolution is not required (e.g. {@code @ResponseBody} method). * has been handled directly and view resolution is not required.
* *
* <p>A default {@link Model} is created at instantiation and used thereafter. * <p>A default {@link Model} is automatically created at instantiation.
* The {@link #setRedirectModel(ModelMap)} method can be used to provide a * An alternate model instance may be provided via {@link #setRedirectModel}
* separate model to use potentially in case of a redirect. * for use in a redirect scenario. When {@link #setUseRedirectModel} is set
* The {@link #setUseRedirectModel()} can be used to enable use of the * to {@code true} signalling a redirect scenario, the {@link #getModel()}
* redirect model if the controller decides to redirect. * returns the redirect model instead of the default model.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
@ -44,12 +44,14 @@ public class ModelAndViewContainer {
private Object view; private Object view;
private boolean resolveView = true; private boolean requestHandled = false;
private final ModelMap model = new BindingAwareModelMap(); private final ModelMap model = new BindingAwareModelMap();
private ModelMap redirectModel; private ModelMap redirectModel;
private boolean ignoreDefaultModelOnRedirect = false;
private boolean useRedirectModel = false; private boolean useRedirectModel = false;
/** /**
@ -99,7 +101,7 @@ public class ModelAndViewContainer {
} }
/** /**
* Whether view resolution is required or not. * Signal a scenario where the request is handled directly.
* <p>A {@link HandlerMethodReturnValueHandler} may use this flag to * <p>A {@link HandlerMethodReturnValueHandler} may use this flag to
* indicate the response has been fully handled and view resolution * indicate the response has been fully handled and view resolution
* is not required (e.g. {@code @ResponseBody}). * is not required (e.g. {@code @ResponseBody}).
@ -109,54 +111,64 @@ public class ModelAndViewContainer {
* a complete response depending on the method return value. * a complete response depending on the method return value.
* <p>The default value is {@code true}. * <p>The default value is {@code true}.
*/ */
public void setResolveView(boolean resolveView) { public void setRequestHandled(boolean requestHandled) {
this.resolveView = resolveView; this.requestHandled = requestHandled;
} }
/** /**
* Whether view resolution is required or not. * Whether the request is handled directly.
*/ */
public boolean isResolveView() { public boolean isRequestHandled() {
return this.resolveView; return this.requestHandled;
} }
/** /**
* Return the default model created at instantiation or the one provided * Return the model to use. This is either the default model created at
* via {@link #setRedirectModel(ModelMap)} as long as it has been enabled * instantiation or the redirect model if {@link #setUseRedirectModel}
* via {@link #setUseRedirectModel()}. * is set to {@code true}. If a redirect model was never provided via
* {@link #setRedirectModel}, return the default model unless
* {@link #setIgnoreDefaultModelOnRedirect} is set to {@code true}.
*/ */
public ModelMap getModel() { public ModelMap getModel() {
if ((this.redirectModel != null) && this.useRedirectModel) { if (!this.useRedirectModel) {
return this.model;
}
else if (this.redirectModel != null) {
return this.redirectModel; return this.redirectModel;
} }
else { else {
return this.model; return this.ignoreDefaultModelOnRedirect ? new ModelMap() : this.model;
} }
} }
/** /**
* Provide a model instance to use in case the controller redirects. * Provide a separate model instance to use in a redirect scenario.
* Note that {@link #setUseRedirectModel()} must also be called in order * The provided additional model however is not used used unless
* to enable use of the redirect model. * {@link #setUseRedirectModel(boolean)} gets set to {@code true} to signal
* a redirect scenario.
*/ */
public void setRedirectModel(ModelMap redirectModel) { public void setRedirectModel(ModelMap redirectModel) {
this.redirectModel = redirectModel; this.redirectModel = redirectModel;
} }
/** /**
* Return the redirect model provided via * When set to {@code true} the default model is never used in a redirect
* {@link #setRedirectModel(ModelMap)} or {@code null} if not provided. * scenario. So if a redirect model is not available, an empty model is
* used instead.
* <p>When set to {@code false} the default model can be used in a redirect
* scenario if a redirect model is not available.
* <p>The default setting is {@code false}.
*/ */
public ModelMap getRedirectModel() { public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {
return this.redirectModel; this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;
} }
/** /**
* Indicate that the redirect model provided via * Signal the conditions for using a redirect model are in place -- e.g.
* {@link #setRedirectModel(ModelMap)} should be used. * the controller has requested a redirect.
*/ */
public void setUseRedirectModel() { public void setUseRedirectModel(boolean useRedirectModel) {
this.useRedirectModel = true; this.useRedirectModel = useRedirectModel;
} }
/** /**
@ -210,7 +222,7 @@ public class ModelAndViewContainer {
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder("ModelAndViewContainer: "); StringBuilder sb = new StringBuilder("ModelAndViewContainer: ");
if (isResolveView()) { if (!isRequestHandled()) {
if (isViewReference()) { if (isViewReference()) {
sb.append("reference to view with name '").append(this.view).append("'"); sb.append("reference to view with name '").append(this.view).append("'");
} }
@ -220,7 +232,7 @@ public class ModelAndViewContainer {
sb.append("; model is ").append(getModel()); sb.append("; model is ").append(getModel());
} }
else { else {
sb.append("View resolution not required"); sb.append("Request handled directly");
} }
return sb.toString(); return sb.toString();
} }

View File

@ -724,29 +724,4 @@ public abstract class WebUtils {
return urlPath.substring(begin, end); return urlPath.substring(begin, end);
} }
/**
* Extracts the path from the given URL by removing the query at the end
* and the scheme and authority in the front, if present.
* @param url a URL, never {@code null}
* @return the extracted URL path
*/
public static String extractUrlPath(String url) {
// Remove query/fragment
int end = url.indexOf('?');
if (end == -1) {
end = url.indexOf('#');
if (end == -1) {
end = url.length();
}
}
url = url.substring(0, end);
// Remove scheme + authority
int start = url.indexOf("://");
if (start != -1) {
start = url.indexOf('/', start + 3);
url = (start != -1 ) ? url.substring(start) : "";
}
return url;
}
} }

View File

@ -0,0 +1,75 @@
/*
* 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.support;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.springframework.ui.ModelMap;
/**
* Test fixture for {@link ModelAndViewContainer}.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
public class ModelAndViewContainerTests {
private ModelAndViewContainer mavContainer;
@Before
public void setup() {
this.mavContainer = new ModelAndViewContainer();
}
@Test
public void getModel() {
this.mavContainer.addAttribute("name", "value");
assertEquals(1, this.mavContainer.getModel().size());
}
@Test
public void getModelRedirectModel() {
ModelMap redirectModel = new ModelMap("name", "redirectValue");
this.mavContainer.setRedirectModel(redirectModel);
this.mavContainer.addAttribute("name", "value");
assertEquals("Default model should be used if not in redirect scenario",
"value", this.mavContainer.getModel().get("name"));
this.mavContainer.setUseRedirectModel(true);
assertEquals("Redirect model should be used in redirect scenario",
"redirectValue", this.mavContainer.getModel().get("name"));
}
@Test
public void getModelIgnoreDefaultModelOnRedirect() {
this.mavContainer.addAttribute("name", "value");
this.mavContainer.setUseRedirectModel(true);
assertEquals("Default model should be used since no redirect model was provided",
1, this.mavContainer.getModel().size());
this.mavContainer.setIgnoreDefaultModelOnRedirect(true);
assertEquals("Empty model should be returned if no redirect model is available",
0, this.mavContainer.getModel().size());
}
}

View File

@ -64,15 +64,4 @@ public class WebUtilsTests {
assertEquals("view.html", WebUtils.extractFullFilenameFromUrlPath("/products/view.html?param=/path/a.do")); assertEquals("view.html", WebUtils.extractFullFilenameFromUrlPath("/products/view.html?param=/path/a.do"));
} }
@Test
public void extractUriPath() {
assertEquals("", WebUtils.extractUrlPath("http://example.com"));
assertEquals("/", WebUtils.extractUrlPath("http://example.com/"));
assertEquals("/rfc/rfc3986.txt", WebUtils.extractUrlPath("http://www.ietf.org/rfc/rfc3986.txt"));
assertEquals("/over/there", WebUtils.extractUrlPath("http://example.com/over/there?name=ferret#nose"));
assertEquals("/over/there", WebUtils.extractUrlPath("http://example.com/over/there#nose"));
assertEquals("/over/there", WebUtils.extractUrlPath("/over/there?name=ferret#nose"));
assertEquals("/over/there", WebUtils.extractUrlPath("/over/there?url=http://example.com"));
}
} }