SPR-5624 - A default HandlerExceptionResolver that resolves standard Spring exceptions
This commit is contained in:
parent
f92b7f1011
commit
161c926054
|
|
@ -22,7 +22,6 @@ import java.util.Collections;
|
|||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
|
@ -48,7 +47,6 @@ import org.springframework.core.io.support.PropertiesLoaderUtils;
|
|||
import org.springframework.ui.context.ThemeSource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.multipart.MultipartResolver;
|
||||
|
|
@ -57,88 +55,65 @@ import org.springframework.web.util.UrlPathHelper;
|
|||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Central dispatcher for HTTP request handlers/controllers,
|
||||
* e.g. for web UI controllers or HTTP-based remote service exporters.
|
||||
* Dispatches to registered handlers for processing a web request,
|
||||
* providing convenient mapping and exception handling facilities.
|
||||
* Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service
|
||||
* exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception
|
||||
* handling facilities.
|
||||
*
|
||||
* <p>This servlet is very flexible: It can be used with just about any workflow,
|
||||
* with the installation of the appropriate adapter classes. It offers the
|
||||
* following functionality that distinguishes it from other request-driven
|
||||
* <p>This servlet is very flexible: It can be used with just about any workflow, with the installation of the
|
||||
* appropriate adapter classes. It offers the following functionality that distinguishes it from other request-driven
|
||||
* web MVC frameworks:
|
||||
*
|
||||
* <ul>
|
||||
* <li>It is based around a JavaBeans configuration mechanism.
|
||||
* <ul> <li>It is based around a JavaBeans configuration mechanism.
|
||||
*
|
||||
* <li>It can use any {@link HandlerMapping} implementation - pre-built or provided
|
||||
* as part of an application - to control the routing of requests to handler objects.
|
||||
* Default is {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping} and
|
||||
* {@link org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping}.
|
||||
* HandlerMapping objects can be defined as beans in the servlet's application context,
|
||||
* implementing the HandlerMapping interface, overriding the default HandlerMapping if present.
|
||||
* HandlerMappings can be given any bean name (they are tested by type).
|
||||
* <li>It can use any {@link HandlerMapping} implementation - pre-built or provided as part of an application - to
|
||||
* control the routing of requests to handler objects. Default is {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}
|
||||
* and {@link org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping}. HandlerMapping objects
|
||||
* can be defined as beans in the servlet's application context, implementing the HandlerMapping interface, overriding
|
||||
* the default HandlerMapping if present. HandlerMappings can be given any bean name (they are tested by type).
|
||||
*
|
||||
* <li>It can use any {@link HandlerAdapter}; this allows for using any handler interface.
|
||||
* Default adapters are {@link org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter},
|
||||
* {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter},
|
||||
* for Spring's {@link org.springframework.web.HttpRequestHandler} and
|
||||
* {@link org.springframework.web.servlet.mvc.Controller} interfaces, respectively. A default
|
||||
* {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter}
|
||||
* will be registered as well. HandlerAdapter objects can be added as beans in the
|
||||
* application context, overriding the default HandlerAdapters. Like HandlerMappings,
|
||||
* HandlerAdapters can be given any bean name (they are tested by type).
|
||||
* <li>It can use any {@link HandlerAdapter}; this allows for using any handler interface. Default adapters are {@link
|
||||
* org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter}, {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter},
|
||||
* for Spring's {@link org.springframework.web.HttpRequestHandler} and {@link org.springframework.web.servlet.mvc.Controller}
|
||||
* interfaces, respectively. A default {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter}
|
||||
* will be registered as well. HandlerAdapter objects can be added as beans in the application context, overriding the
|
||||
* default HandlerAdapters. Like HandlerMappings, HandlerAdapters can be given any bean name (they are tested by type).
|
||||
*
|
||||
* <li>The dispatcher's exception resolution strategy can be specified via a
|
||||
* {@link HandlerExceptionResolver}, for example mapping certain exceptions to
|
||||
* error pages. Default is none. Additional HandlerExceptionResolvers can be added
|
||||
* through the application context. HandlerExceptionResolver can be given any
|
||||
* bean name (they are tested by type).
|
||||
* <li>The dispatcher's exception resolution strategy can be specified via a {@link HandlerExceptionResolver}, for
|
||||
* example mapping certain exceptions to error pages. Default is none. Additional HandlerExceptionResolvers can be added
|
||||
* through the application context. HandlerExceptionResolver can be given any bean name (they are tested by type).
|
||||
*
|
||||
* <li>Its view resolution strategy can be specified via a {@link ViewResolver}
|
||||
* implementation, resolving symbolic view names into View objects. Default is
|
||||
* {@link org.springframework.web.servlet.view.InternalResourceViewResolver}.
|
||||
* ViewResolver objects can be added as beans in the application context,
|
||||
* overriding the default ViewResolver. ViewResolvers can be given any bean name
|
||||
* (they are tested by type).
|
||||
* <li>Its view resolution strategy can be specified via a {@link ViewResolver} implementation, resolving symbolic view
|
||||
* names into View objects. Default is {@link org.springframework.web.servlet.view.InternalResourceViewResolver}.
|
||||
* ViewResolver objects can be added as beans in the application context, overriding the default ViewResolver.
|
||||
* ViewResolvers can be given any bean name (they are tested by type).
|
||||
*
|
||||
* <li>If a {@link View} or view name is not supplied by the user, then the configured
|
||||
* {@link RequestToViewNameTranslator} will translate the current request into a
|
||||
* view name. The corresponding bean name is "viewNameTranslator"; the default is
|
||||
* {@link org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator}.
|
||||
* <li>If a {@link View} or view name is not supplied by the user, then the configured {@link
|
||||
* RequestToViewNameTranslator} will translate the current request into a view name. The corresponding bean name is
|
||||
* "viewNameTranslator"; the default is {@link org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator}.
|
||||
*
|
||||
* <li>The dispatcher's strategy for resolving multipart requests is determined by
|
||||
* a {@link org.springframework.web.multipart.MultipartResolver} implementation.
|
||||
* Implementations for Jakarta Commons FileUpload and Jason Hunter's COS are
|
||||
* included; the typical choise is
|
||||
* {@link org.springframework.web.multipart.commons.CommonsMultipartResolver}.
|
||||
* <li>The dispatcher's strategy for resolving multipart requests is determined by a {@link
|
||||
* org.springframework.web.multipart.MultipartResolver} implementation. Implementations for Jakarta Commons FileUpload
|
||||
* and Jason Hunter's COS are included; the typical choise is {@link org.springframework.web.multipart.commons.CommonsMultipartResolver}.
|
||||
* The MultipartResolver bean name is "multipartResolver"; default is none.
|
||||
*
|
||||
* <li>Its locale resolution strategy is determined by a {@link LocaleResolver}.
|
||||
* Out-of-the-box implementations work via HTTP accept header, cookie, or session.
|
||||
* The LocaleResolver bean name is "localeResolver"; default is
|
||||
* {@link org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver}.
|
||||
* <li>Its locale resolution strategy is determined by a {@link LocaleResolver}. Out-of-the-box implementations work via
|
||||
* HTTP accept header, cookie, or session. The LocaleResolver bean name is "localeResolver"; default is {@link
|
||||
* org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver}.
|
||||
*
|
||||
* <li>Its theme resolution strategy is determined by a {@link ThemeResolver}.
|
||||
* Implementations for a fixed theme and for cookie and session storage are included.
|
||||
* The ThemeResolver bean name is "themeResolver"; default is
|
||||
* {@link org.springframework.web.servlet.theme.FixedThemeResolver}.
|
||||
* </ul>
|
||||
* <li>Its theme resolution strategy is determined by a {@link ThemeResolver}. Implementations for a fixed theme and for
|
||||
* cookie and session storage are included. The ThemeResolver bean name is "themeResolver"; default is {@link
|
||||
* org.springframework.web.servlet.theme.FixedThemeResolver}. </ul>
|
||||
*
|
||||
* <p><b>NOTE: The <code>@RequestMapping</code> annotation will only be processed
|
||||
* if a corresponding <code>HandlerMapping</code> (for type level annotations)
|
||||
* and/or <code>HandlerAdapter</code> (for method level annotations)
|
||||
* is present in the dispatcher.</b> This is the case by default.
|
||||
* However, if you are defining custom <code>HandlerMappings</code> or
|
||||
* <code>HandlerAdapters</code>, then you need to make sure that a
|
||||
* corresponding custom <code>DefaultAnnotationHandlerMapping</code>
|
||||
* and/or <code>AnnotationMethodHandlerAdapter</code> is defined as well
|
||||
* - provided that you intend to use <code>@RequestMapping</code>.
|
||||
* <p><b>NOTE: The <code>@RequestMapping</code> annotation will only be processed if a corresponding
|
||||
* <code>HandlerMapping</code> (for type level annotations) and/or <code>HandlerAdapter</code> (for method level
|
||||
* annotations) is present in the dispatcher.</b> This is the case by default. However, if you are defining custom
|
||||
* <code>HandlerMappings</code> or <code>HandlerAdapters</code>, then you need to make sure that a corresponding custom
|
||||
* <code>DefaultAnnotationHandlerMapping</code> and/or <code>AnnotationMethodHandlerAdapter</code> is defined as well -
|
||||
* provided that you intend to use <code>@RequestMapping</code>.
|
||||
*
|
||||
* <p><b>A web application can define any number of DispatcherServlets.</b>
|
||||
* Each servlet will operate in its own namespace, loading its own application
|
||||
* context with mappings, handlers, etc. Only the root application context
|
||||
* as loaded by {@link org.springframework.web.context.ContextLoaderListener},
|
||||
* if any, will be shared.
|
||||
* <p><b>A web application can define any number of DispatcherServlets.</b> Each servlet will operate in its own
|
||||
* namespace, loading its own application context with mappings, handlers, etc. Only the root application context as
|
||||
* loaded by {@link org.springframework.web.context.ContextLoaderListener}, if any, will be shared.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
|
|
@ -149,102 +124,92 @@ import org.springframework.web.util.WebUtils;
|
|||
*/
|
||||
public class DispatcherServlet extends FrameworkServlet {
|
||||
|
||||
/**
|
||||
* Well-known name for the MultipartResolver object in the bean factory for this namespace.
|
||||
*/
|
||||
/** Well-known name for the MultipartResolver object in the bean factory for this namespace. */
|
||||
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
|
||||
|
||||
/**
|
||||
* Well-known name for the LocaleResolver object in the bean factory for this namespace.
|
||||
*/
|
||||
/** Well-known name for the LocaleResolver object in the bean factory for this namespace. */
|
||||
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
|
||||
|
||||
/**
|
||||
* Well-known name for the ThemeResolver object in the bean factory for this namespace.
|
||||
*/
|
||||
/** Well-known name for the ThemeResolver object in the bean factory for this namespace. */
|
||||
public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
|
||||
|
||||
/**
|
||||
* Well-known name for the HandlerMapping object in the bean factory for this namespace.
|
||||
* Only used when "detectAllHandlerMappings" is turned off.
|
||||
* Well-known name for the HandlerMapping object in the bean factory for this namespace. Only used when
|
||||
* "detectAllHandlerMappings" is turned off.
|
||||
*
|
||||
* @see #setDetectAllHandlerMappings
|
||||
*/
|
||||
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
|
||||
|
||||
/**
|
||||
* Well-known name for the HandlerAdapter object in the bean factory for this namespace.
|
||||
* Only used when "detectAllHandlerAdapters" is turned off.
|
||||
* Well-known name for the HandlerAdapter object in the bean factory for this namespace. Only used when
|
||||
* "detectAllHandlerAdapters" is turned off.
|
||||
*
|
||||
* @see #setDetectAllHandlerAdapters
|
||||
*/
|
||||
public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
|
||||
|
||||
/**
|
||||
* Well-known name for the HandlerExceptionResolver object in the bean factory for this
|
||||
* namespace. Only used when "detectAllHandlerExceptionResolvers" is turned off.
|
||||
* Well-known name for the HandlerExceptionResolver object in the bean factory for this namespace. Only used when
|
||||
* "detectAllHandlerExceptionResolvers" is turned off.
|
||||
*
|
||||
* @see #setDetectAllHandlerExceptionResolvers
|
||||
*/
|
||||
public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
|
||||
|
||||
/**
|
||||
* Well-known name for the RequestToViewNameTranslator object in the bean factory for
|
||||
* this namespace.
|
||||
*/
|
||||
/** Well-known name for the RequestToViewNameTranslator object in the bean factory for this namespace. */
|
||||
public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
|
||||
|
||||
/**
|
||||
* Well-known name for the ViewResolver object in the bean factory for this namespace.
|
||||
* Only used when "detectAllViewResolvers" is turned off.
|
||||
* Well-known name for the ViewResolver object in the bean factory for this namespace. Only used when
|
||||
* "detectAllViewResolvers" is turned off.
|
||||
*
|
||||
* @see #setDetectAllViewResolvers
|
||||
*/
|
||||
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
|
||||
|
||||
/**
|
||||
* Request attribute to hold the currently chosen HandlerExecutionChain.
|
||||
* Only used for internal optimizations.
|
||||
*/
|
||||
/** Request attribute to hold the currently chosen HandlerExecutionChain. Only used for internal optimizations. */
|
||||
public static final String HANDLER_EXECUTION_CHAIN_ATTRIBUTE = DispatcherServlet.class.getName() + ".HANDLER";
|
||||
|
||||
/**
|
||||
* Request attribute to hold the current web application context.
|
||||
* Otherwise only the global web app context is obtainable by tags etc.
|
||||
* Request attribute to hold the current web application context. Otherwise only the global web app context is
|
||||
* obtainable by tags etc.
|
||||
*
|
||||
* @see org.springframework.web.servlet.support.RequestContextUtils#getWebApplicationContext
|
||||
*/
|
||||
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
|
||||
|
||||
/**
|
||||
* Request attribute to hold the current LocaleResolver, retrievable by views.
|
||||
*
|
||||
* @see org.springframework.web.servlet.support.RequestContextUtils#getLocaleResolver
|
||||
*/
|
||||
public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
|
||||
|
||||
/**
|
||||
* Request attribute to hold the current ThemeResolver, retrievable by views.
|
||||
*
|
||||
* @see org.springframework.web.servlet.support.RequestContextUtils#getThemeResolver
|
||||
*/
|
||||
public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
|
||||
|
||||
/**
|
||||
* Request attribute to hold the current ThemeSource, retrievable by views.
|
||||
*
|
||||
* @see org.springframework.web.servlet.support.RequestContextUtils#getThemeSource
|
||||
*/
|
||||
public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
|
||||
|
||||
|
||||
/**
|
||||
* Log category to use when no mapped handler is found for a request.
|
||||
*/
|
||||
/** Log category to use when no mapped handler is found for a request. */
|
||||
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
|
||||
|
||||
/**
|
||||
* Name of the class path resource (relative to the DispatcherServlet class)
|
||||
* that defines DispatcherServlet's default strategy names.
|
||||
* Name of the class path resource (relative to the DispatcherServlet class) that defines DispatcherServlet's default
|
||||
* strategy names.
|
||||
*/
|
||||
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
|
||||
|
||||
|
||||
/**
|
||||
* Additional logger to use when no mapped handler is found for a request.
|
||||
*/
|
||||
/** Additional logger to use when no mapped handler is found for a request. */
|
||||
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
|
||||
|
||||
private static final Properties defaultStrategies;
|
||||
|
|
@ -262,7 +227,6 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
|
||||
private boolean detectAllHandlerMappings = true;
|
||||
|
||||
|
|
@ -278,7 +242,6 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
/** Perform cleanup of request attributes after include request? */
|
||||
private boolean cleanupAfterInclude = true;
|
||||
|
||||
|
||||
/** MultipartResolver used by this servlet */
|
||||
private MultipartResolver multipartResolver;
|
||||
|
||||
|
|
@ -303,80 +266,65 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
/** List of ViewResolvers used by this servlet */
|
||||
private List<ViewResolver> viewResolvers;
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to detect all HandlerMapping beans in this servlet's context.
|
||||
* Else, just a single bean with name "handlerMapping" will be expected.
|
||||
* <p>Default is "true". Turn this off if you want this servlet to use a
|
||||
* single HandlerMapping, despite multiple HandlerMapping beans being
|
||||
* defined in the context.
|
||||
* Set whether to detect all HandlerMapping beans in this servlet's context. Else, just a single bean with name
|
||||
* "handlerMapping" will be expected. <p>Default is "true". Turn this off if you want this servlet to use a single
|
||||
* HandlerMapping, despite multiple HandlerMapping beans being defined in the context.
|
||||
*/
|
||||
public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) {
|
||||
this.detectAllHandlerMappings = detectAllHandlerMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to detect all HandlerAdapter beans in this servlet's context.
|
||||
* Else, just a single bean with name "handlerAdapter" will be expected.
|
||||
* <p>Default is "true". Turn this off if you want this servlet to use a
|
||||
* single HandlerAdapter, despite multiple HandlerAdapter beans being
|
||||
* defined in the context.
|
||||
* Set whether to detect all HandlerAdapter beans in this servlet's context. Else, just a single bean with name
|
||||
* "handlerAdapter" will be expected. <p>Default is "true". Turn this off if you want this servlet to use a single
|
||||
* HandlerAdapter, despite multiple HandlerAdapter beans being defined in the context.
|
||||
*/
|
||||
public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) {
|
||||
this.detectAllHandlerAdapters = detectAllHandlerAdapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to detect all HandlerExceptionResolver beans in this servlet's context.
|
||||
* Else, just a single bean with name "handlerExceptionResolver" will be expected.
|
||||
* <p>Default is "true". Turn this off if you want this servlet to use a
|
||||
* single HandlerExceptionResolver, despite multiple HandlerExceptionResolver
|
||||
* beans being defined in the context.
|
||||
* Set whether to detect all HandlerExceptionResolver beans in this servlet's context. Else, just a single bean with
|
||||
* name "handlerExceptionResolver" will be expected. <p>Default is "true". Turn this off if you want this servlet to
|
||||
* use a single HandlerExceptionResolver, despite multiple HandlerExceptionResolver beans being defined in the
|
||||
* context.
|
||||
*/
|
||||
public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) {
|
||||
this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to detect all ViewResolver beans in this servlet's context.
|
||||
* Else, just a single bean with name "viewResolver" will be expected.
|
||||
* <p>Default is "true". Turn this off if you want this servlet to use a
|
||||
* single ViewResolver, despite multiple ViewResolver beans being
|
||||
* defined in the context.
|
||||
* Set whether to detect all ViewResolver beans in this servlet's context. Else, just a single bean with name
|
||||
* "viewResolver" will be expected. <p>Default is "true". Turn this off if you want this servlet to use a single
|
||||
* ViewResolver, despite multiple ViewResolver beans being defined in the context.
|
||||
*/
|
||||
public void setDetectAllViewResolvers(boolean detectAllViewResolvers) {
|
||||
this.detectAllViewResolvers = detectAllViewResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to perform cleanup of request attributes after an include request,
|
||||
* that is, whether to reset the original state of all request attributes after
|
||||
* the DispatcherServlet has processed within an include request. Else, just the
|
||||
* DispatcherServlet's own request attributes will be reset, but not model
|
||||
* attributes for JSPs or special attributes set by views (for example, JSTL's).
|
||||
* <p>Default is "true", which is strongly recommended. Views should not rely on
|
||||
* request attributes having been set by (dynamic) includes. This allows JSP views
|
||||
* rendered by an included controller to use any model attributes, even with the
|
||||
* same names as in the main JSP, without causing side effects. Only turn this
|
||||
* off for special needs, for example to deliberately allow main JSPs to access
|
||||
* attributes from JSP views rendered by an included controller.
|
||||
* Set whether to perform cleanup of request attributes after an include request, that is, whether to reset the
|
||||
* original state of all request attributes after the DispatcherServlet has processed within an include request. Else,
|
||||
* just the DispatcherServlet's own request attributes will be reset, but not model attributes for JSPs or special
|
||||
* attributes set by views (for example, JSTL's). <p>Default is "true", which is strongly recommended. Views should not
|
||||
* rely on request attributes having been set by (dynamic) includes. This allows JSP views rendered by an included
|
||||
* controller to use any model attributes, even with the same names as in the main JSP, without causing side effects.
|
||||
* Only turn this off for special needs, for example to deliberately allow main JSPs to access attributes from JSP
|
||||
* views rendered by an included controller.
|
||||
*/
|
||||
public void setCleanupAfterInclude(boolean cleanupAfterInclude) {
|
||||
this.cleanupAfterInclude = cleanupAfterInclude;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation calls {@link #initStrategies}.
|
||||
*/
|
||||
/** This implementation calls {@link #initStrategies}. */
|
||||
@Override
|
||||
protected void onRefresh(ApplicationContext context) throws BeansException {
|
||||
initStrategies(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the strategy objects that this servlet uses.
|
||||
* <p>May be overridden in subclasses in order to initialize
|
||||
* Initialize the strategy objects that this servlet uses. <p>May be overridden in subclasses in order to initialize
|
||||
* further strategy objects.
|
||||
*/
|
||||
protected void initStrategies(ApplicationContext context) {
|
||||
|
|
@ -391,8 +339,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize the MultipartResolver used by this class.
|
||||
* <p>If no bean is defined with the given name in the BeanFactory
|
||||
* Initialize the MultipartResolver used by this class. <p>If no bean is defined with the given name in the BeanFactory
|
||||
* for this namespace, no multipart handling is provided.
|
||||
*/
|
||||
private void initMultipartResolver(ApplicationContext context) {
|
||||
|
|
@ -406,15 +353,14 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
// Default is no multipart resolver.
|
||||
this.multipartResolver = null;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
|
||||
logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
|
||||
"': no multipart request handling provided");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the LocaleResolver used by this class.
|
||||
* <p>If no bean is defined with the given name in the BeanFactory
|
||||
* Initialize the LocaleResolver used by this class. <p>If no bean is defined with the given name in the BeanFactory
|
||||
* for this namespace, we default to AcceptHeaderLocaleResolver.
|
||||
*/
|
||||
private void initLocaleResolver(ApplicationContext context) {
|
||||
|
|
@ -435,9 +381,8 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize the ThemeResolver used by this class.
|
||||
* <p>If no bean is defined with the given name in the BeanFactory
|
||||
* for this namespace, we default to a FixedThemeResolver.
|
||||
* Initialize the ThemeResolver used by this class. <p>If no bean is defined with the given name in the BeanFactory for
|
||||
* this namespace, we default to a FixedThemeResolver.
|
||||
*/
|
||||
private void initThemeResolver(ApplicationContext context) {
|
||||
try {
|
||||
|
|
@ -450,24 +395,24 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
// We need to use the default.
|
||||
this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME +
|
||||
"': using default [" + this.themeResolver + "]");
|
||||
logger.debug(
|
||||
"Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME + "': using default [" +
|
||||
this.themeResolver + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the HandlerMappings used by this class.
|
||||
* <p>If no HandlerMapping beans are defined in the BeanFactory
|
||||
* for this namespace, we default to BeanNameUrlHandlerMapping.
|
||||
* Initialize the HandlerMappings used by this class. <p>If no HandlerMapping beans are defined in the BeanFactory for
|
||||
* this namespace, we default to BeanNameUrlHandlerMapping.
|
||||
*/
|
||||
private void initHandlerMappings(ApplicationContext context) {
|
||||
this.handlerMappings = null;
|
||||
|
||||
if (this.detectAllHandlerMappings) {
|
||||
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
|
||||
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
context, HandlerMapping.class, true, false);
|
||||
Map<String, HandlerMapping> matchingBeans =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
|
||||
if (!matchingBeans.isEmpty()) {
|
||||
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
|
||||
// We keep HandlerMappings in sorted order.
|
||||
|
|
@ -495,17 +440,16 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize the HandlerAdapters used by this class.
|
||||
* <p>If no HandlerAdapter beans are defined in the BeanFactory
|
||||
* for this namespace, we default to SimpleControllerHandlerAdapter.
|
||||
* Initialize the HandlerAdapters used by this class. <p>If no HandlerAdapter beans are defined in the BeanFactory for
|
||||
* this namespace, we default to SimpleControllerHandlerAdapter.
|
||||
*/
|
||||
private void initHandlerAdapters(ApplicationContext context) {
|
||||
this.handlerAdapters = null;
|
||||
|
||||
if (this.detectAllHandlerAdapters) {
|
||||
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
|
||||
Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
context, HandlerAdapter.class, true, false);
|
||||
Map<String, HandlerAdapter> matchingBeans =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
|
||||
if (!matchingBeans.isEmpty()) {
|
||||
this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
|
||||
// We keep HandlerAdapters in sorted order.
|
||||
|
|
@ -533,17 +477,16 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize the HandlerExceptionResolver used by this class.
|
||||
* <p>If no bean is defined with the given name in the BeanFactory
|
||||
* for this namespace, we default to no exception resolver.
|
||||
* Initialize the HandlerExceptionResolver used by this class. <p>If no bean is defined with the given name in the
|
||||
* BeanFactory for this namespace, we default to no exception resolver.
|
||||
*/
|
||||
private void initHandlerExceptionResolvers(ApplicationContext context) {
|
||||
this.handlerExceptionResolvers = null;
|
||||
|
||||
if (this.detectAllHandlerExceptionResolvers) {
|
||||
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
|
||||
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
context, HandlerExceptionResolver.class, true, false);
|
||||
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
|
||||
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
|
||||
if (!matchingBeans.isEmpty()) {
|
||||
this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
|
||||
// We keep HandlerExceptionResolvers in sorted order.
|
||||
|
|
@ -552,8 +495,8 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
else {
|
||||
try {
|
||||
HandlerExceptionResolver her = context.getBean(
|
||||
HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
|
||||
HandlerExceptionResolver her =
|
||||
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
|
||||
this.handlerExceptionResolvers = Collections.singletonList(her);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
|
|
@ -561,8 +504,8 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
}
|
||||
|
||||
// Just for consistency, check for default HandlerExceptionResolvers...
|
||||
// There aren't any in usual scenarios.
|
||||
// Ensure we have at least some HandlerExceptionResolvers, by registering
|
||||
// default HandlerExceptionResolvers if no other resolvers are found.
|
||||
if (this.handlerExceptionResolvers == null) {
|
||||
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
|
@ -572,13 +515,13 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize the RequestToViewNameTranslator used by this servlet instance. If no
|
||||
* implementation is configured then we default to DefaultRequestToViewNameTranslator.
|
||||
* Initialize the RequestToViewNameTranslator used by this servlet instance. If no implementation is configured then we
|
||||
* default to DefaultRequestToViewNameTranslator.
|
||||
*/
|
||||
private void initRequestToViewNameTranslator(ApplicationContext context) {
|
||||
try {
|
||||
this.viewNameTranslator = context.getBean(
|
||||
REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
|
||||
this.viewNameTranslator =
|
||||
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using RequestToViewNameTranslator [" + this.viewNameTranslator + "]");
|
||||
}
|
||||
|
|
@ -588,24 +531,23 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unable to locate RequestToViewNameTranslator with name '" +
|
||||
REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
|
||||
"': using default [" + this.viewNameTranslator + "]");
|
||||
REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME + "': using default [" + this.viewNameTranslator +
|
||||
"]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the ViewResolvers used by this class.
|
||||
* <p>If no ViewResolver beans are defined in the BeanFactory
|
||||
* for this namespace, we default to InternalResourceViewResolver.
|
||||
* Initialize the ViewResolvers used by this class. <p>If no ViewResolver beans are defined in the BeanFactory for this
|
||||
* namespace, we default to InternalResourceViewResolver.
|
||||
*/
|
||||
private void initViewResolvers(ApplicationContext context) {
|
||||
this.viewResolvers = null;
|
||||
|
||||
if (this.detectAllViewResolvers) {
|
||||
// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
|
||||
Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
context, ViewResolver.class, true, false);
|
||||
Map<String, ViewResolver> matchingBeans =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
|
||||
if (!matchingBeans.isEmpty()) {
|
||||
this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.values());
|
||||
// We keep ViewResolvers in sorted order.
|
||||
|
|
@ -633,9 +575,9 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return this servlet's ThemeSource, if any; else return <code>null</code>.
|
||||
* <p>Default is to return the WebApplicationContext as ThemeSource,
|
||||
* provided that it implements the ThemeSource interface.
|
||||
* Return this servlet's ThemeSource, if any; else return <code>null</code>. <p>Default is to return the
|
||||
* WebApplicationContext as ThemeSource, provided that it implements the ThemeSource interface.
|
||||
*
|
||||
* @return the ThemeSource, if any
|
||||
* @see #getWebApplicationContext()
|
||||
*/
|
||||
|
|
@ -650,18 +592,18 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
|
||||
/**
|
||||
* Obtain this servlet's MultipartResolver, if any.
|
||||
* @return the MultipartResolver used by this servlet, or <code>null</code>
|
||||
* if none (indicating that no multipart support is available)
|
||||
*
|
||||
* @return the MultipartResolver used by this servlet, or <code>null</code> if none (indicating that no multipart
|
||||
* support is available)
|
||||
*/
|
||||
public final MultipartResolver getMultipartResolver() {
|
||||
return this.multipartResolver;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the default strategy object for the given strategy interface.
|
||||
* <p>The default implementation delegates to {@link #getDefaultStrategies},
|
||||
* expecting a single object in the list.
|
||||
* Return the default strategy object for the given strategy interface. <p>The default implementation delegates to
|
||||
* {@link #getDefaultStrategies}, expecting a single object in the list.
|
||||
*
|
||||
* @param context the current WebApplicationContext
|
||||
* @param strategyInterface the strategy interface
|
||||
* @return the corresponding strategy object
|
||||
|
|
@ -677,10 +619,10 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a List of default strategy objects for the given strategy interface.
|
||||
* <p>The default implementation uses the "DispatcherServlet.properties" file
|
||||
* (in the same package as the DispatcherServlet class) to determine the class names.
|
||||
* It instantiates the strategy objects through the context's BeanFactory.
|
||||
* Create a List of default strategy objects for the given strategy interface. <p>The default implementation uses the
|
||||
* "DispatcherServlet.properties" file (in the same package as the DispatcherServlet class) to determine the class
|
||||
* names. It instantiates the strategy objects through the context's BeanFactory.
|
||||
*
|
||||
* @param context the current WebApplicationContext
|
||||
* @param strategyInterface the strategy interface
|
||||
* @return the List of corresponding strategy objects
|
||||
|
|
@ -717,13 +659,12 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a default strategy.
|
||||
* <p>The default implementation uses
|
||||
* {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}.
|
||||
* Create a default strategy. <p>The default implementation uses {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}.
|
||||
*
|
||||
* @param context the current WebApplicationContext
|
||||
* @param clazz the strategy implementation class to instantiate
|
||||
* @throws BeansException if initialization failed
|
||||
* @return the fully configured strategy instance
|
||||
* @throws BeansException if initialization failed
|
||||
* @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean
|
||||
*/
|
||||
|
|
@ -731,17 +672,16 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
return context.getAutowireCapableBeanFactory().createBean(clazz);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exposes the DispatcherServlet-specific request attributes and
|
||||
* delegates to {@link #doDispatch} for the actual dispatching.
|
||||
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch} for the actual
|
||||
* dispatching.
|
||||
*/
|
||||
@Override
|
||||
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
String requestUri = new UrlPathHelper().getRequestUri(request);
|
||||
logger.debug("DispatcherServlet with name '" + getServletName() +
|
||||
"' processing " + request.getMethod() + " request for [" + requestUri + "]");
|
||||
logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
|
||||
" request for [" + requestUri + "]");
|
||||
}
|
||||
|
||||
// Keep a snapshot of the request attributes in case of an include,
|
||||
|
|
@ -777,12 +717,11 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Process the actual dispatching to the handler.
|
||||
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
|
||||
* The HandlerAdapter will be obtained by querying the servlet's installed
|
||||
* HandlerAdapters to find the first that supports the handler class.
|
||||
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or
|
||||
* handlers themselves to decide which methods are acceptable.
|
||||
* Process the actual dispatching to the handler. <p>The handler will be obtained by applying the servlet's
|
||||
* HandlerMappings in order. The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters to
|
||||
* find the first that supports the handler class. <p>All HTTP methods are handled by this method. It's up to
|
||||
* HandlerAdapters or handlers themselves to decide which methods are acceptable.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @throws Exception in case of any kind of processing failure
|
||||
|
|
@ -855,8 +794,8 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" +
|
||||
getServletName() + "': assuming HandlerAdapter completed request handling");
|
||||
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
|
||||
"': assuming HandlerAdapter completed request handling");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -885,15 +824,16 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Override HttpServlet's <code>getLastModified</code> method to evaluate
|
||||
* the Last-Modified value of the mapped handler.
|
||||
* Override HttpServlet's <code>getLastModified</code> method to evaluate the Last-Modified value of the mapped
|
||||
* handler.
|
||||
*/
|
||||
@Override
|
||||
protected long getLastModified(HttpServletRequest request) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
String requestUri = new UrlPathHelper().getRequestUri(request);
|
||||
logger.debug("DispatcherServlet with name '" + getServletName() +
|
||||
"' determining Last-Modified value for [" + requestUri + "]");
|
||||
logger.debug(
|
||||
"DispatcherServlet with name '" + getServletName() + "' determining Last-Modified value for [" +
|
||||
requestUri + "]");
|
||||
}
|
||||
try {
|
||||
HandlerExecutionChain mappedHandler = getHandler(request, true);
|
||||
|
|
@ -918,12 +858,11 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build a LocaleContext for the given request, exposing the request's
|
||||
* primary locale as current locale.
|
||||
* <p>The default implementation uses the dispatcher's LocaleResolver
|
||||
* to obtain the current locale, which might change during a request.
|
||||
* Build a LocaleContext for the given request, exposing the request's primary locale as current locale. <p>The default
|
||||
* implementation uses the dispatcher's LocaleResolver to obtain the current locale, which might change during a
|
||||
* request.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the corresponding LocaleContext
|
||||
*/
|
||||
|
|
@ -933,6 +872,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
public Locale getLocale() {
|
||||
return localeResolver.resolveLocale(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getLocale().toString();
|
||||
|
|
@ -941,8 +881,9 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convert the request into a multipart request, and make multipart resolver available.
|
||||
* If no multipart resolver is set, simply use the existing request.
|
||||
* Convert the request into a multipart request, and make multipart resolver available. If no multipart resolver is
|
||||
* set, simply use the existing request.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the processed request (multipart wrapper if necessary)
|
||||
* @see MultipartResolver#resolveMultipart
|
||||
|
|
@ -963,6 +904,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
|
||||
/**
|
||||
* Clean up any resources used by the given multipart request (if any).
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @see MultipartResolver#cleanupMultipart
|
||||
*/
|
||||
|
|
@ -973,15 +915,14 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the HandlerExecutionChain for this request.
|
||||
* Try all handler mappings in order.
|
||||
* Return the HandlerExecutionChain for this request. Try all handler mappings in order.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param cache whether to cache the HandlerExecutionChain in a request attribute
|
||||
* @return the HandlerExceutionChain, or <code>null</code> if no handler could be found
|
||||
*/
|
||||
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
|
||||
HandlerExecutionChain handler =
|
||||
(HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
|
||||
HandlerExecutionChain handler = (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
|
||||
if (handler != null) {
|
||||
if (!cache) {
|
||||
request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
|
||||
|
|
@ -1007,6 +948,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
|
||||
/**
|
||||
* No handler found -> set appropriate HTTP response status.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @throws Exception if preparing the response failed
|
||||
|
|
@ -1014,17 +956,17 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
if (pageNotFoundLogger.isWarnEnabled()) {
|
||||
String requestUri = new UrlPathHelper().getRequestUri(request);
|
||||
pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" +
|
||||
requestUri + "] in DispatcherServlet with name '" + getServletName() + "'");
|
||||
pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + requestUri +
|
||||
"] in DispatcherServlet with name '" + getServletName() + "'");
|
||||
}
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HandlerAdapter for this handler object.
|
||||
*
|
||||
* @param handler the handler object to find an adapter for
|
||||
* @throws ServletException if no HandlerAdapter can be found for the handler.
|
||||
* This is a fatal error.
|
||||
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
|
||||
*/
|
||||
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
|
||||
for (HandlerAdapter ha : this.handlerAdapters) {
|
||||
|
|
@ -1041,17 +983,19 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
|
||||
/**
|
||||
* Determine an error ModelAndView via the registered HandlerExceptionResolvers.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of
|
||||
* the exception (for example, if multipart resolution failed)
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @return a corresponding ModelAndView to forward to
|
||||
* @throws Exception if no error ModelAndView found
|
||||
*/
|
||||
protected ModelAndView processHandlerException(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
|
||||
throws Exception {
|
||||
protected ModelAndView processHandlerException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler,
|
||||
Exception ex) throws Exception {
|
||||
|
||||
// Check registerer HandlerExceptionResolvers...
|
||||
ModelAndView exMv = null;
|
||||
|
|
@ -1066,35 +1010,26 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
return null;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
|
||||
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv,
|
||||
ex);
|
||||
}
|
||||
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
|
||||
return exMv;
|
||||
}
|
||||
|
||||
// Send default responses for well-known exceptions, if possible.
|
||||
if (ex instanceof HttpRequestMethodNotSupportedException && !response.isCommitted()) {
|
||||
String[] supportedMethods = ((HttpRequestMethodNotSupportedException) ex).getSupportedMethods();
|
||||
if (supportedMethods != null) {
|
||||
response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
|
||||
}
|
||||
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
throw ex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the given ModelAndView. This is the last stage in handling a request.
|
||||
* It may involve resolving the view by name.
|
||||
* Render the given ModelAndView. This is the last stage in handling a request. It may involve resolving the view by
|
||||
* name.
|
||||
*
|
||||
* @param mv the ModelAndView to render
|
||||
* @param request current HTTP servlet request
|
||||
* @param response current HTTP servlet response
|
||||
* @throws Exception if there's a problem rendering the view
|
||||
*/
|
||||
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
// Determine locale for request and apply it to the response.
|
||||
Locale locale = this.localeResolver.resolveLocale(request);
|
||||
|
|
@ -1106,8 +1041,9 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
// We need to resolve the view name.
|
||||
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
|
||||
if (view == null) {
|
||||
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
|
||||
"' in servlet with name '" + getServletName() + "'");
|
||||
throw new ServletException(
|
||||
"Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" +
|
||||
getServletName() + "'");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -1128,6 +1064,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
|
||||
/**
|
||||
* Translate the supplied request into a default view name.
|
||||
*
|
||||
* @param request current HTTP servlet request
|
||||
* @return the view name (or <code>null</code> if no default found)
|
||||
* @throws Exception if view name translation failed
|
||||
|
|
@ -1137,22 +1074,22 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Resolve the given view name into a View object (to be rendered).
|
||||
* <p>Default implementations asks all ViewResolvers of this dispatcher.
|
||||
* Can be overridden for custom resolution strategies, potentially based
|
||||
* on specific model attributes or request parameters.
|
||||
* Resolve the given view name into a View object (to be rendered). <p>Default implementations asks all ViewResolvers
|
||||
* of this dispatcher. Can be overridden for custom resolution strategies, potentially based on specific model
|
||||
* attributes or request parameters.
|
||||
*
|
||||
* @param viewName the name of the view to resolve
|
||||
* @param model the model to be passed to the view
|
||||
* @param locale the current locale
|
||||
* @param request current HTTP servlet request
|
||||
* @return the View object, or <code>null</code> if none found
|
||||
* @throws Exception if the view cannot be resolved
|
||||
* (typically in case of problems creating an actual View object)
|
||||
* @throws Exception if the view cannot be resolved (typically in case of problems creating an actual View object)
|
||||
* @see ViewResolver#resolveViewName
|
||||
*/
|
||||
protected View resolveViewName(
|
||||
String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request)
|
||||
throws Exception {
|
||||
protected View resolveViewName(String viewName,
|
||||
Map<String, Object> model,
|
||||
Locale locale,
|
||||
HttpServletRequest request) throws Exception {
|
||||
|
||||
for (ViewResolver viewResolver : this.viewResolvers) {
|
||||
View view = viewResolver.resolveViewName(viewName, locale);
|
||||
|
|
@ -1164,18 +1101,19 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
|
||||
* Will just invoke afterCompletion for all interceptors whose preHandle
|
||||
* invocation has successfully completed and returned true.
|
||||
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors. Will just invoke afterCompletion for all
|
||||
* interceptors whose preHandle invocation has successfully completed and returned true.
|
||||
*
|
||||
* @param mappedHandler the mapped HandlerExecutionChain
|
||||
* @param interceptorIndex index of last interceptor that successfully completed
|
||||
* @param ex Exception thrown on handler execution, or <code>null</code> if none
|
||||
* @see HandlerInterceptor#afterCompletion
|
||||
*/
|
||||
private void triggerAfterCompletion(
|
||||
HandlerExecutionChain mappedHandler, int interceptorIndex,
|
||||
HttpServletRequest request, HttpServletResponse response, Exception ex)
|
||||
throws Exception {
|
||||
private void triggerAfterCompletion(HandlerExecutionChain mappedHandler,
|
||||
int interceptorIndex,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Exception ex) throws Exception {
|
||||
|
||||
// Apply afterCompletion methods of registered interceptors.
|
||||
if (mappedHandler != null) {
|
||||
|
|
@ -1196,9 +1134,9 @@ public class DispatcherServlet extends FrameworkServlet {
|
|||
|
||||
/**
|
||||
* Restore the request attributes after an include.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param attributesSnapshot the snapshot of the request attributes
|
||||
* before the include
|
||||
* @param attributesSnapshot the snapshot of the request attributes before the include
|
||||
*/
|
||||
private void restoreAttributesAfterInclude(HttpServletRequest request, Map attributesSnapshot) {
|
||||
logger.debug("Restoring snapshot of request attributes after include");
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.m
|
|||
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
|
||||
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
|
||||
|
||||
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.handler.DefaultHandlerExceptionResolver
|
||||
|
||||
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
|
||||
|
||||
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link HandlerExceptionResolver} implementations. <p>Provides a set of mapped handlers that
|
||||
* the resolver should map to, and the {@link Ordered} implementation.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private Set mappedHandlers;
|
||||
|
||||
private Class[] mappedHandlerClasses;
|
||||
|
||||
private Log warnLogger;
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the set of handlers that this exception resolver should apply to. The exception mappings and the default
|
||||
* error view will only apply to the specified handlers. <p>If no handlers and handler classes are set, the exception
|
||||
* mappings and the default error view will apply to all handlers. This means that a specified default error view will
|
||||
* be used as fallback for all exceptions; any further HandlerExceptionResolvers in the chain will be ignored in this
|
||||
* case.
|
||||
*/
|
||||
public void setMappedHandlers(Set mappedHandlers) {
|
||||
this.mappedHandlers = mappedHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the set of classes that this exception resolver should apply to. The exception mappings and the default
|
||||
* error view will only apply to handlers of the specified type; the specified types may be interfaces and superclasses
|
||||
* of handlers as well. <p>If no handlers and handler classes are set, the exception mappings and the default error
|
||||
* view will apply to all handlers. This means that a specified default error view will be used as fallback for all
|
||||
* exceptions; any further HandlerExceptionResolvers in the chain will be ignored in this case.
|
||||
*/
|
||||
public void setMappedHandlerClasses(Class[] mappedHandlerClasses) {
|
||||
this.mappedHandlerClasses = mappedHandlerClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the log category for warn logging. The name will be passed to the underlying logger implementation through
|
||||
* Commons Logging, getting interpreted as log category according to the logger's configuration. <p>Default is no warn
|
||||
* logging. Specify this setting to activate warn logging into a specific category. Alternatively, override the {@link
|
||||
* #logException} method for custom logging.
|
||||
*
|
||||
* @see org.apache.commons.logging.LogFactory#getLog(String)
|
||||
* @see org.apache.log4j.Logger#getLogger(String)
|
||||
* @see java.util.logging.Logger#getLogger(String)
|
||||
*/
|
||||
public void setWarnLogCategory(String loggerName) {
|
||||
this.warnLogger = LogFactory.getLog(loggerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this resolver is supposed to apply (i.e. the handler matches in case of "mappedHandlers" having been
|
||||
* specified), then delegates to the {@link #doResolveException} template method.
|
||||
*/
|
||||
public ModelAndView resolveException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler,
|
||||
Exception ex) {
|
||||
|
||||
if (shouldApplyTo(request, handler)) {
|
||||
// Log exception, both at debug log level and at warn level, if desired.
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
|
||||
}
|
||||
logException(ex, request);
|
||||
return doResolveException(request, response, handler, ex);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this resolver is supposed to apply to the given handler. <p>The default implementation checks against
|
||||
* the specified mapped handlers and handler classes, if any.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @return whether this resolved should proceed with resolving the exception for the given request and handler
|
||||
* @see #setMappedHandlers
|
||||
* @see #setMappedHandlerClasses
|
||||
*/
|
||||
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
|
||||
if (handler != null) {
|
||||
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
|
||||
return true;
|
||||
}
|
||||
if (this.mappedHandlerClasses != null) {
|
||||
for (Class handlerClass : this.mappedHandlerClasses) {
|
||||
if (handlerClass.isInstance(handler)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else only apply if there are no explicit handler mappings.
|
||||
return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the given exception at warn level, provided that warn logging has been activated through the {@link
|
||||
* #setWarnLogCategory "warnLogCategory"} property. <p>Calls {@link #buildLogMessage} in order to determine the
|
||||
* concrete message to log. Always passes the full exception to the logger.
|
||||
*
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
* @see #setWarnLogCategory
|
||||
* @see #buildLogMessage
|
||||
* @see org.apache.commons.logging.Log#warn(Object, Throwable)
|
||||
*/
|
||||
protected void logException(Exception ex, HttpServletRequest request) {
|
||||
if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
|
||||
this.warnLogger.warn(buildLogMessage(ex, request), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a log message for the given exception, occured during processing the given request.
|
||||
*
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
* @return the log message to use
|
||||
*/
|
||||
protected String buildLogMessage(Exception ex, HttpServletRequest request) {
|
||||
return "Handler execution resulted in exception";
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually resolve the given exception that got thrown during on handler execution, returning a ModelAndView that
|
||||
* represents a specific error page if appropriate. <p>May be overridden in subclasses, in order to apply specific
|
||||
* exception checks. Note that this template method will be invoked <i>after</i> checking whether this resolved applies
|
||||
* ("mappedHandlers" etc), so an implementation may simply proceed with its actual exception handling.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @return a corresponding ModelAndView to forward to, or <code>null</code> for default processing
|
||||
*/
|
||||
protected abstract ModelAndView doResolveException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler,
|
||||
Exception ex);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link org.springframework.web.servlet.HandlerExceptionResolver
|
||||
* HandlerExceptionResolver} interface that resolves standard Spring exceptions. <p>Default implementations typically
|
||||
* set the response status.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see #handleNoSuchRequestHandlingMethod
|
||||
* @see #handleHttpRequestMethodNotSupported
|
||||
* @see #handleHttpMediaTypeNotSupported
|
||||
* @see #handleMissingServletRequestParameter
|
||||
* @see #handleTypeMismatch
|
||||
* @see #handleHttpMessageNotReadable
|
||||
* @see #handleHttpMessageNotWritable
|
||||
* @since 3.0
|
||||
*/
|
||||
public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||
|
||||
/**
|
||||
* Log category to use when no mapped handler is found for a request.
|
||||
*
|
||||
* @see #pageNotFoundLogger
|
||||
*/
|
||||
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
|
||||
|
||||
/**
|
||||
* Additional logger to use when no mapped handler is found for a request.
|
||||
*
|
||||
* @see #PAGE_NOT_FOUND_LOG_CATEGORY
|
||||
*/
|
||||
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
|
||||
|
||||
/** Sets the {@linkplain #setOrder(int) order} to {@link #LOWEST_PRECEDENCE}. */
|
||||
public DefaultHandlerExceptionResolver() {
|
||||
setOrder(Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView doResolveException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler,
|
||||
Exception ex) {
|
||||
try {
|
||||
if (ex instanceof NoSuchRequestHandlingMethodException) {
|
||||
return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,
|
||||
handler);
|
||||
}
|
||||
else if (ex instanceof HttpRequestMethodNotSupportedException) {
|
||||
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
|
||||
response, handler);
|
||||
}
|
||||
else if (ex instanceof HttpMediaTypeNotSupportedException) {
|
||||
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,
|
||||
handler);
|
||||
}
|
||||
else if (ex instanceof MissingServletRequestParameterException) {
|
||||
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,
|
||||
response, handler);
|
||||
}
|
||||
else if (ex instanceof TypeMismatchException) {
|
||||
return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);
|
||||
}
|
||||
else if (ex instanceof HttpMessageNotReadableException) {
|
||||
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);
|
||||
}
|
||||
else if (ex instanceof HttpMessageNotWritableException) {
|
||||
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);
|
||||
}
|
||||
}
|
||||
catch (Exception handlerException) {
|
||||
logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where no request handler method was found. <p>The default implementation logs a warning, sends an
|
||||
* HTTP 404 error, and returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen, or the
|
||||
* NoSuchRequestHandlingMethodException could be rethrown as-is.
|
||||
*
|
||||
* @param ex the NoSuchRequestHandlingMethodException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleNoSuchRequestHandlingMethod(NoSuchRequestHandlingMethodException ex,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
|
||||
pageNotFoundLogger.warn(ex.getMessage());
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where no request handler method was found for the particular HTTP request method. <p>The default
|
||||
* implementation logs a warning, sends an HTTP 405 error, sets the "Allow" header, and returns an empty {@code
|
||||
* ModelAndView}. Alternatively, a fallback view could be chosen, or the HttpRequestMethodNotSupportedException could
|
||||
* be rethrown as-is.
|
||||
*
|
||||
* @param ex the HttpRequestMethodNotSupportedException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
|
||||
pageNotFoundLogger.warn(ex.getMessage());
|
||||
String[] supportedMethods = ex.getSupportedMethods();
|
||||
if (supportedMethods != null) {
|
||||
response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
|
||||
}
|
||||
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters}
|
||||
* were found for the PUT or POSTed content. <p>The default implementation sends an HTTP 415 error, sets the "Allow"
|
||||
* header, and returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen, or the
|
||||
* HttpMediaTypeNotSupportedException could be rethrown as-is.
|
||||
*
|
||||
* @param ex the HttpMediaTypeNotSupportedException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
|
||||
response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
|
||||
List<MediaType> mediaTypes = ex.getSupportedMediaTypes();
|
||||
if (mediaTypes != null) {
|
||||
response.setHeader("Accept", MediaType.toString(mediaTypes));
|
||||
}
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case when a required parameter is missing. <p>The default implementation sends an HTTP 400 error, and
|
||||
* returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen, or the
|
||||
* MissingServletRequestParameterException could be rethrown as-is.
|
||||
*
|
||||
* @param ex the MissingServletRequestParameterException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case when a {@link org.springframework.web.bind.WebDataBinder} conversion error occurs. <p>The default
|
||||
* implementation sends an HTTP 400 error, and returns an empty {@code ModelAndView}. Alternatively, a fallback view
|
||||
* could be chosen, or the TypeMismatchException could be rethrown as-is.
|
||||
*
|
||||
* @param ex the TypeMismatchException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleTypeMismatch(TypeMismatchException ex,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where a {@linkplain org.springframework.http.converter.HttpMessageConverter message converter} can
|
||||
* not read from a HTTP request. <p>The default implementation sends an HTTP 400 error, and returns an empty {@code
|
||||
* ModelAndView}. Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotSupportedException could be
|
||||
* rethrown as-is.
|
||||
*
|
||||
* @param ex the HttpMessageNotReadableException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where a {@linkplain org.springframework.http.converter.HttpMessageConverter message converter} can
|
||||
* not write to a HTTP request. <p>The default implementation sends an HTTP 500 error, and returns an empty {@code
|
||||
* ModelAndView}. Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotSupportedException could be
|
||||
* rethrown as-is.
|
||||
*
|
||||
* @param ex the HttpMessageNotWritableException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleHttpMessageNotWritable(HttpMessageNotWritableException ex,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,51 +18,29 @@ package org.springframework.web.servlet.handler;
|
|||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.web.servlet.HandlerExceptionResolver} implementation
|
||||
* that allows for mapping exception class names to view names, either for a
|
||||
* set of given handlers or for all handlers in the DispatcherServlet.
|
||||
* {@link org.springframework.web.servlet.HandlerExceptionResolver} implementation that allows for mapping exception
|
||||
* class names to view names, either for a set of given handlers or for all handlers in the DispatcherServlet.
|
||||
*
|
||||
* <p>Error views are analogous to error page JSPs, but can be used with any
|
||||
* kind of exception including any checked one, with fine-granular mappings for
|
||||
* specific handlers.
|
||||
* <p>Error views are analogous to error page JSPs, but can be used with any kind of exception including any checked
|
||||
* one, with fine-granular mappings for specific handlers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 22.11.2003
|
||||
* @author Arjen Poutsma
|
||||
* @see org.springframework.web.servlet.DispatcherServlet
|
||||
* @since 22.11.2003
|
||||
*/
|
||||
public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, Ordered {
|
||||
public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||
|
||||
/**
|
||||
* The default name of the exception attribute: "exception".
|
||||
*/
|
||||
/** The default name of the exception attribute: "exception". */
|
||||
public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
|
||||
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
|
||||
|
||||
private Set mappedHandlers;
|
||||
|
||||
private Class[] mappedHandlerClasses;
|
||||
|
||||
private Log warnLogger;
|
||||
|
||||
private Properties exceptionMappings;
|
||||
|
||||
private String defaultErrorView;
|
||||
|
|
@ -71,74 +49,18 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
|
||||
private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
|
||||
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the set of handlers that this exception resolver should apply to.
|
||||
* The exception mappings and the default error view will only apply
|
||||
* to the specified handlers.
|
||||
* <p>If no handlers and handler classes are set, the exception mappings
|
||||
* and the default error view will apply to all handlers. This means that
|
||||
* a specified default error view will be used as fallback for all exceptions;
|
||||
* any further HandlerExceptionResolvers in the chain will be ignored in
|
||||
* this case.
|
||||
*/
|
||||
public void setMappedHandlers(Set mappedHandlers) {
|
||||
this.mappedHandlers = mappedHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the set of classes that this exception resolver should apply to.
|
||||
* The exception mappings and the default error view will only apply
|
||||
* to handlers of the specified type; the specified types may be interfaces
|
||||
* and superclasses of handlers as well.
|
||||
* <p>If no handlers and handler classes are set, the exception mappings
|
||||
* and the default error view will apply to all handlers. This means that
|
||||
* a specified default error view will be used as fallback for all exceptions;
|
||||
* any further HandlerExceptionResolvers in the chain will be ignored in
|
||||
* this case.
|
||||
*/
|
||||
public void setMappedHandlerClasses(Class[] mappedHandlerClasses) {
|
||||
this.mappedHandlerClasses = mappedHandlerClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the log category for warn logging. The name will be passed to the
|
||||
* underlying logger implementation through Commons Logging, getting
|
||||
* interpreted as log category according to the logger's configuration.
|
||||
* <p>Default is no warn logging. Specify this setting to activate
|
||||
* warn logging into a specific category. Alternatively, override
|
||||
* the {@link #logException} method for custom logging.
|
||||
* @see org.apache.commons.logging.LogFactory#getLog(String)
|
||||
* @see org.apache.log4j.Logger#getLogger(String)
|
||||
* @see java.util.logging.Logger#getLogger(String)
|
||||
*/
|
||||
public void setWarnLogCategory(String loggerName) {
|
||||
this.warnLogger = LogFactory.getLog(loggerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mappings between exception class names and error view names.
|
||||
* The exception class name can be a substring, with no wildcard support
|
||||
* at present. A value of "ServletException" would match
|
||||
* <code>javax.servlet.ServletException</code> and subclasses, for example.
|
||||
* <p><b>NB:</b> Consider carefully how specific the pattern is, and whether
|
||||
* to include package information (which isn't mandatory). For example,
|
||||
* "Exception" will match nearly anything, and will probably hide other rules.
|
||||
* "java.lang.Exception" would be correct if "Exception" was meant to define
|
||||
* a rule for all checked exceptions. With more unusual exception names such
|
||||
* as "BaseBusinessException" there's no need to use a FQN.
|
||||
* <p>Follows the same matching algorithm as RuleBasedTransactionAttribute
|
||||
* and RollbackRuleAttribute.
|
||||
* @param mappings exception patterns (can also be fully qualified class names)
|
||||
* as keys, and error view names as values
|
||||
* Set the mappings between exception class names and error view names. The exception class name can be a substring,
|
||||
* with no wildcard support at present. A value of "ServletException" would match
|
||||
* <code>javax.servlet.ServletException</code> and subclasses, for example. <p><b>NB:</b> Consider carefully how
|
||||
* specific the pattern is, and whether to include package information (which isn't mandatory). For example,
|
||||
* "Exception" will match nearly anything, and will probably hide other rules. "java.lang.Exception" would be correct
|
||||
* if "Exception" was meant to define a rule for all checked exceptions. With more unusual exception names such as
|
||||
* "BaseBusinessException" there's no need to use a FQN. <p>Follows the same matching algorithm as
|
||||
* RuleBasedTransactionAttribute and RollbackRuleAttribute.
|
||||
*
|
||||
* @param mappings exception patterns (can also be fully qualified class names) as keys, and error view names as
|
||||
* values
|
||||
* @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
|
||||
* @see org.springframework.transaction.interceptor.RollbackRuleAttribute
|
||||
*/
|
||||
|
|
@ -147,24 +69,20 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the name of the default error view.
|
||||
* This view will be returned if no specific mapping was found.
|
||||
* <p>Default is none.
|
||||
* Set the name of the default error view. This view will be returned if no specific mapping was found. <p>Default is
|
||||
* none.
|
||||
*/
|
||||
public void setDefaultErrorView(String defaultErrorView) {
|
||||
this.defaultErrorView = defaultErrorView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default HTTP status code that this exception resolver will apply
|
||||
* if it resolves an error view.
|
||||
* <p>Note that this error code will only get applied in case of a top-level
|
||||
* request. It will not be set for an include request, since the HTTP status
|
||||
* cannot be modified from within an include.
|
||||
* <p>If not specified, no status code will be applied, either leaving this to
|
||||
* the controller or view, or keeping the servlet engine's default of 200 (OK).
|
||||
* @param defaultStatusCode HTTP status code value, for example
|
||||
* 500 (SC_INTERNAL_SERVER_ERROR) or 404 (SC_NOT_FOUND)
|
||||
* Set the default HTTP status code that this exception resolver will apply if it resolves an error view. <p>Note that
|
||||
* this error code will only get applied in case of a top-level request. It will not be set for an include request,
|
||||
* since the HTTP status cannot be modified from within an include. <p>If not specified, no status code will be
|
||||
* applied, either leaving this to the controller or view, or keeping the servlet engine's default of 200 (OK).
|
||||
*
|
||||
* @param defaultStatusCode HTTP status code value, for example 500 (SC_INTERNAL_SERVER_ERROR) or 404 (SC_NOT_FOUND)
|
||||
* @see javax.servlet.http.HttpServletResponse#SC_INTERNAL_SERVER_ERROR
|
||||
* @see javax.servlet.http.HttpServletResponse#SC_NOT_FOUND
|
||||
*/
|
||||
|
|
@ -173,84 +91,33 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the name of the model attribute as which the exception should
|
||||
* be exposed. Default is "exception".
|
||||
* <p>This can be either set to a different attribute name or to
|
||||
* <code>null</code> for not exposing an exception attribute at all.
|
||||
* Set the name of the model attribute as which the exception should be exposed. Default is "exception". <p>This can be
|
||||
* either set to a different attribute name or to <code>null</code> for not exposing an exception attribute at all.
|
||||
*
|
||||
* @see #DEFAULT_EXCEPTION_ATTRIBUTE
|
||||
*/
|
||||
public void setExceptionAttribute(String exceptionAttribute) {
|
||||
this.exceptionAttribute = exceptionAttribute;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether this resolver is supposed to apply (i.e. the handler
|
||||
* matches in case of "mappedHandlers" having been specified), then
|
||||
* delegates to the {@link #doResolveException} template method.
|
||||
*/
|
||||
public ModelAndView resolveException(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
|
||||
if (shouldApplyTo(request, handler)) {
|
||||
return doResolveException(request, response, handler, ex);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this resolver is supposed to apply to the given handler.
|
||||
* <p>The default implementation checks against the specified mapped handlers
|
||||
* and handler classes, if any.
|
||||
* @param request current HTTP request
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the
|
||||
* time of the exception (for example, if multipart resolution failed)
|
||||
* @return whether this resolved should proceed with resolving the exception
|
||||
* for the given request and handler
|
||||
* @see #setMappedHandlers
|
||||
* @see #setMappedHandlerClasses
|
||||
*/
|
||||
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
|
||||
if (handler != null) {
|
||||
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
|
||||
return true;
|
||||
}
|
||||
if (this.mappedHandlerClasses != null) {
|
||||
for (Class handlerClass : this.mappedHandlerClasses) {
|
||||
if (handlerClass.isInstance(handler)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else only apply if there are no explicit handler mappings.
|
||||
return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually resolve the given exception that got thrown during on handler execution,
|
||||
* returning a ModelAndView that represents a specific error page if appropriate.
|
||||
* <p>May be overridden in subclasses, in order to apply specific exception checks.
|
||||
* Note that this template method will be invoked <i>after</i> checking whether
|
||||
* this resolved applies ("mappedHandlers" etc), so an implementation may simply
|
||||
* proceed with its actual exception handling.
|
||||
* Actually resolve the given exception that got thrown during on handler execution, returning a ModelAndView that
|
||||
* represents a specific error page if appropriate. <p>May be overridden in subclasses, in order to apply specific
|
||||
* exception checks. Note that this template method will be invoked <i>after</i> checking whether this resolved applies
|
||||
* ("mappedHandlers" etc), so an implementation may simply proceed with its actual exception handling.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the
|
||||
* time of the exception (for example, if multipart resolution failed)
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the time of the exception (for example,
|
||||
* if multipart resolution failed)
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @return a corresponding ModelAndView to forward to, or <code>null</code> for default processing
|
||||
*/
|
||||
protected ModelAndView doResolveException(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
|
||||
// Log exception, both at debug log level and at warn level, if desired.
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
|
||||
}
|
||||
logException(ex, request);
|
||||
@Override
|
||||
protected ModelAndView doResolveException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler,
|
||||
Exception ex) {
|
||||
|
||||
// Expose ModelAndView for chosen error view.
|
||||
String viewName = determineViewName(ex, request);
|
||||
|
|
@ -268,40 +135,10 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log the given exception at warn level, provided that warn logging has been
|
||||
* activated through the {@link #setWarnLogCategory "warnLogCategory"} property.
|
||||
* <p>Calls {@link #buildLogMessage} in order to determine the concrete message
|
||||
* to log. Always passes the full exception to the logger.
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
* @see #setWarnLogCategory
|
||||
* @see #buildLogMessage
|
||||
* @see org.apache.commons.logging.Log#warn(Object, Throwable)
|
||||
*/
|
||||
protected void logException(Exception ex, HttpServletRequest request) {
|
||||
if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
|
||||
this.warnLogger.warn(buildLogMessage(ex, request), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a log message for the given exception, occured during processing
|
||||
* the given request.
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
* @return the log message to use
|
||||
*/
|
||||
protected String buildLogMessage(Exception ex, HttpServletRequest request) {
|
||||
return "Handler execution resulted in exception";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the view name for the given exception, searching the
|
||||
* {@link #setExceptionMappings "exceptionMappings"}, using the
|
||||
* {@link #setDefaultErrorView "defaultErrorView"} as fallback.
|
||||
* Determine the view name for the given exception, searching the {@link #setExceptionMappings "exceptionMappings"},
|
||||
* using the {@link #setDefaultErrorView "defaultErrorView"} as fallback.
|
||||
*
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
* @return the resolved view name, or <code>null</code> if none found
|
||||
|
|
@ -315,8 +152,8 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
// Return default error view else, if defined.
|
||||
if (viewName == null && this.defaultErrorView != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resolving to default view '" + this.defaultErrorView +
|
||||
"' for exception of type [" + ex.getClass().getName() + "]");
|
||||
logger.debug("Resolving to default view '" + this.defaultErrorView + "' for exception of type [" +
|
||||
ex.getClass().getName() + "]");
|
||||
}
|
||||
viewName = this.defaultErrorView;
|
||||
}
|
||||
|
|
@ -325,6 +162,7 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
|
||||
/**
|
||||
* Find a matching view name in the given exception mappings.
|
||||
*
|
||||
* @param exceptionMappings mappings between exception class names and error view names
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @return the view name, or <code>null</code> if none found
|
||||
|
|
@ -351,11 +189,9 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the depth to the superclass matching.
|
||||
* <p>0 means ex matches exactly. Returns -1 if there's no match.
|
||||
* Otherwise, returns depth. Lowest depth wins.
|
||||
* <p>Follows the same algorithm as
|
||||
* {@link org.springframework.transaction.interceptor.RollbackRuleAttribute}.
|
||||
* Return the depth to the superclass matching. <p>0 means ex matches exactly. Returns -1 if there's no match.
|
||||
* Otherwise, returns depth. Lowest depth wins. <p>Follows the same algorithm as {@link
|
||||
* org.springframework.transaction.interceptor.RollbackRuleAttribute}.
|
||||
*/
|
||||
protected int getDepth(String exceptionMapping, Exception ex) {
|
||||
return getDepth(exceptionMapping, ex.getClass(), 0);
|
||||
|
|
@ -373,17 +209,15 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the HTTP status code to apply for the given error view.
|
||||
* <p>The default implementation always returns the specified
|
||||
* {@link #setDefaultStatusCode "defaultStatusCode"}, as a common
|
||||
* status code for all error views. Override this in a custom subclass
|
||||
* to determine a specific status code for the given view.
|
||||
* Determine the HTTP status code to apply for the given error view. <p>The default implementation always returns the
|
||||
* specified {@link #setDefaultStatusCode "defaultStatusCode"}, as a common status code for all error views. Override
|
||||
* this in a custom subclass to determine a specific status code for the given view.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param viewName the name of the error view
|
||||
* @return the HTTP status code to use, or <code>null</code> for the
|
||||
* servlet container's default (200 in case of a standard error view)
|
||||
* @return the HTTP status code to use, or <code>null</code> for the servlet container's default (200 in case of a
|
||||
* standard error view)
|
||||
* @see #setDefaultStatusCode
|
||||
* @see #applyStatusCodeIfPossible
|
||||
*/
|
||||
|
|
@ -392,8 +226,9 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
}
|
||||
|
||||
/**
|
||||
* Apply the specified HTTP status code to the given response, if possible
|
||||
* (that is, if not executing within an include request).
|
||||
* Apply the specified HTTP status code to the given response, if possible (that is, if not executing within an include
|
||||
* request).
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param statusCode the status code to apply
|
||||
|
|
@ -412,8 +247,9 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a ModelAndView for the given request, view name and exception.
|
||||
* <p>The default implementation delegates to {@link #getModelAndView(String, Exception)}.
|
||||
* Return a ModelAndView for the given request, view name and exception. <p>The default implementation delegates to
|
||||
* {@link #getModelAndView(String, Exception)}.
|
||||
*
|
||||
* @param viewName the name of the error view
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
|
|
@ -424,9 +260,9 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a ModelAndView for the given view name and exception.
|
||||
* <p>The default implementation adds the specified exception attribute.
|
||||
* Can be overridden in subclasses.
|
||||
* Return a ModelAndView for the given view name and exception. <p>The default implementation adds the specified
|
||||
* exception attribute. Can be overridden in subclasses.
|
||||
*
|
||||
* @param viewName the name of the error view
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @return the ModelAndView instance
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ import org.springframework.core.MethodParameter;
|
|||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.FormHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
|
|
@ -68,7 +67,6 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.support.BindingAwareModelMap;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.HttpSessionRequiredException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
|
|
@ -101,38 +99,36 @@ import org.springframework.web.util.UrlPathHelper;
|
|||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link org.springframework.web.servlet.HandlerAdapter}
|
||||
* interface that maps handler methods based on HTTP paths, HTTP methods and
|
||||
* request parameters expressed through the {@link RequestMapping} annotation.
|
||||
* Implementation of the {@link org.springframework.web.servlet.HandlerAdapter} interface that maps handler methods
|
||||
* based on HTTP paths, HTTP methods and request parameters expressed through the {@link RequestMapping} annotation.
|
||||
*
|
||||
* <p>Supports request parameter binding through the {@link RequestParam} annotation.
|
||||
* Also supports the {@link ModelAttribute} annotation for exposing model attribute
|
||||
* values to the view, as well as {@link InitBinder} for binder initialization methods
|
||||
* and {@link SessionAttributes} for automatic session management of specific attributes.
|
||||
* <p>Supports request parameter binding through the {@link RequestParam} annotation. Also supports the {@link
|
||||
* ModelAttribute} annotation for exposing model attribute values to the view, as well as {@link InitBinder} for binder
|
||||
* initialization methods and {@link SessionAttributes} for automatic session management of specific attributes.
|
||||
*
|
||||
* <p>This adapter can be customized through various bean properties.
|
||||
* A common use case is to apply shared binder initialization logic through
|
||||
* a custom {@link #setWebBindingInitializer WebBindingInitializer}.
|
||||
* <p>This adapter can be customized through various bean properties. A common use case is to apply shared binder
|
||||
* initialization logic through a custom {@link #setWebBindingInitializer WebBindingInitializer}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @since 2.5
|
||||
* @see #setPathMatcher
|
||||
* @see #setMethodNameResolver
|
||||
* @see #setWebBindingInitializer
|
||||
* @see #setSessionAttributeStore
|
||||
* @since 2.5
|
||||
*/
|
||||
public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter {
|
||||
|
||||
/**
|
||||
* Log category to use when no mapped handler is found for a request.
|
||||
*
|
||||
* @see #pageNotFoundLogger
|
||||
*/
|
||||
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
|
||||
|
||||
|
||||
/**
|
||||
* Additional logger to use when no mapped handler is found for a request.
|
||||
*
|
||||
* @see #PAGE_NOT_FOUND_LOG_CATEGORY
|
||||
*/
|
||||
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
|
||||
|
|
@ -167,12 +163,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
super(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set if URL lookup should always use the full path within the current servlet
|
||||
* context. Else, the path within the current servlet mapping is used if applicable
|
||||
* (that is, in the case of a ".../*" servlet mapping in web.xml).
|
||||
* Set if URL lookup should always use the full path within the current servlet context. Else, the path within the
|
||||
* current servlet mapping is used if applicable (that is, in the case of a ".../*" servlet mapping in web.xml).
|
||||
* <p>Default is "false".
|
||||
*
|
||||
* @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
|
||||
*/
|
||||
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
||||
|
|
@ -180,10 +175,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Set if context path and request URI should be URL-decoded. Both are returned
|
||||
* <i>undecoded</i> by the Servlet API, in contrast to the servlet path.
|
||||
* <p>Uses either the request encoding or the default encoding according
|
||||
* to the Servlet spec (ISO-8859-1).
|
||||
* Set if context path and request URI should be URL-decoded. Both are returned <i>undecoded</i> by the Servlet API, in
|
||||
* contrast to the servlet path. <p>Uses either the request encoding or the default encoding according to the Servlet
|
||||
* spec (ISO-8859-1).
|
||||
*
|
||||
* @see org.springframework.web.util.UrlPathHelper#setUrlDecode
|
||||
*/
|
||||
public void setUrlDecode(boolean urlDecode) {
|
||||
|
|
@ -191,10 +186,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the UrlPathHelper to use for resolution of lookup paths.
|
||||
* <p>Use this to override the default UrlPathHelper with a custom subclass,
|
||||
* or to share common UrlPathHelper settings across multiple HandlerMappings
|
||||
* and HandlerAdapters.
|
||||
* Set the UrlPathHelper to use for resolution of lookup paths. <p>Use this to override the default UrlPathHelper with
|
||||
* a custom subclass, or to share common UrlPathHelper settings across multiple HandlerMappings and HandlerAdapters.
|
||||
*/
|
||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
||||
|
|
@ -202,8 +195,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the PathMatcher implementation to use for matching URL paths
|
||||
* against registered URL patterns. Default is AntPathMatcher.
|
||||
* Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. Default is
|
||||
* AntPathMatcher.
|
||||
*
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
|
|
@ -212,9 +206,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the MethodNameResolver to use for resolving default handler methods
|
||||
* (carrying an empty <code>@RequestMapping</code> annotation).
|
||||
* <p>Will only kick in when the handler method cannot be resolved uniquely
|
||||
* Set the MethodNameResolver to use for resolving default handler methods (carrying an empty
|
||||
* <code>@RequestMapping</code> annotation). <p>Will only kick in when the handler method cannot be resolved uniquely
|
||||
* through the annotation metadata already.
|
||||
*/
|
||||
public void setMethodNameResolver(MethodNameResolver methodNameResolver) {
|
||||
|
|
@ -222,18 +215,16 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Specify a WebBindingInitializer which will apply pre-configured
|
||||
* configuration to every DataBinder that this controller uses.
|
||||
* Specify a WebBindingInitializer which will apply pre-configured configuration to every DataBinder that this
|
||||
* controller uses.
|
||||
*/
|
||||
public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
|
||||
this.webBindingInitializer = webBindingInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the strategy to store session attributes with.
|
||||
* <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
|
||||
* storing session attributes in the HttpSession, using the same
|
||||
* attribute name as in the model.
|
||||
* Specify the strategy to store session attributes with. <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
|
||||
* storing session attributes in the HttpSession, using the same attribute name as in the model.
|
||||
*/
|
||||
public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
|
||||
Assert.notNull(sessionAttributeStore, "SessionAttributeStore must not be null");
|
||||
|
|
@ -241,11 +232,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Cache content produced by <code>@SessionAttributes</code> annotated handlers
|
||||
* for the given number of seconds. Default is 0, preventing caching completely.
|
||||
* <p>In contrast to the "cacheSeconds" property which will apply to all general
|
||||
* handlers (but not to <code>@SessionAttributes</code> annotated handlers), this
|
||||
* setting will apply to <code>@SessionAttributes</code> annotated handlers only.
|
||||
* Cache content produced by <code>@SessionAttributes</code> annotated handlers for the given number of seconds.
|
||||
* Default is 0, preventing caching completely. <p>In contrast to the "cacheSeconds" property which will apply to all
|
||||
* general handlers (but not to <code>@SessionAttributes</code> annotated handlers), this setting will apply to
|
||||
* <code>@SessionAttributes</code> annotated handlers only.
|
||||
*
|
||||
* @see #setCacheSeconds
|
||||
* @see org.springframework.web.bind.annotation.SessionAttributes
|
||||
*/
|
||||
|
|
@ -254,20 +245,15 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Set if controller execution should be synchronized on the session,
|
||||
* to serialize parallel invocations from the same client.
|
||||
* <p>More specifically, the execution of each handler method will get
|
||||
* synchronized if this flag is "true". The best available session mutex
|
||||
* will be used for the synchronization; ideally, this will be a mutex
|
||||
* exposed by HttpSessionMutexListener.
|
||||
* <p>The session mutex is guaranteed to be the same object during
|
||||
* the entire lifetime of the session, available under the key defined
|
||||
* by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a
|
||||
* safe reference to synchronize on for locking on the current session.
|
||||
* <p>In many cases, the HttpSession reference itself is a safe mutex
|
||||
* as well, since it will always be the same object reference for the
|
||||
* same active logical session. However, this is not guaranteed across
|
||||
* different servlet containers; the only 100% safe way is a session mutex.
|
||||
* Set if controller execution should be synchronized on the session, to serialize parallel invocations from the same
|
||||
* client. <p>More specifically, the execution of each handler method will get synchronized if this flag is "true". The
|
||||
* best available session mutex will be used for the synchronization; ideally, this will be a mutex exposed by
|
||||
* HttpSessionMutexListener. <p>The session mutex is guaranteed to be the same object during the entire lifetime of the
|
||||
* session, available under the key defined by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a safe
|
||||
* reference to synchronize on for locking on the current session. <p>In many cases, the HttpSession reference itself
|
||||
* is a safe mutex as well, since it will always be the same object reference for the same active logical session.
|
||||
* However, this is not guaranteed across different servlet containers; the only 100% safe way is a session mutex.
|
||||
*
|
||||
* @see org.springframework.web.util.HttpSessionMutexListener
|
||||
* @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
|
||||
*/
|
||||
|
|
@ -276,44 +262,38 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the ParameterNameDiscoverer to use for resolving method parameter
|
||||
* names if needed (e.g. for default attribute names).
|
||||
* <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
||||
* Set the ParameterNameDiscoverer to use for resolving method parameter names if needed (e.g. for default attribute
|
||||
* names). <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
||||
*/
|
||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom ArgumentResolvers to use for special method parameter types.
|
||||
* Such a custom ArgumentResolver will kick in first, having a chance to
|
||||
* resolve an argument value before the standard argument handling kicks in.
|
||||
* Set a custom ArgumentResolvers to use for special method parameter types. Such a custom ArgumentResolver will kick
|
||||
* in first, having a chance to resolve an argument value before the standard argument handling kicks in.
|
||||
*/
|
||||
public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
|
||||
this.customArgumentResolvers = new WebArgumentResolver[] {argumentResolver};
|
||||
this.customArgumentResolvers = new WebArgumentResolver[]{argumentResolver};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more custom ArgumentResolvers to use for special method
|
||||
* parameter types. Any such custom ArgumentResolver will kick in first,
|
||||
* having a chance to resolve an argument value before the standard
|
||||
* argument handling kicks in.
|
||||
* Set one or more custom ArgumentResolvers to use for special method parameter types. Any such custom ArgumentResolver
|
||||
* will kick in first, having a chance to resolve an argument value before the standard argument handling kicks in.
|
||||
*/
|
||||
public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
|
||||
this.customArgumentResolvers = argumentResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message body converters to use. These converters are used to convert
|
||||
* from and to HTTP requests and responses.
|
||||
* Set the message body converters to use. These converters are used to convert from and to HTTP requests and
|
||||
* responses.
|
||||
*/
|
||||
public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
|
||||
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean supports(Object handler) {
|
||||
return getMethodResolver(handler).hasHandlerMethods();
|
||||
}
|
||||
|
|
@ -345,32 +325,20 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
return invokeHandlerMethod(request, response, handler);
|
||||
}
|
||||
|
||||
protected ModelAndView invokeHandlerMethod(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
|
||||
try {
|
||||
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
|
||||
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
|
||||
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
|
||||
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
||||
ExtendedModelMap implicitModel = new BindingAwareModelMap();
|
||||
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
|
||||
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
|
||||
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
|
||||
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
||||
ExtendedModelMap implicitModel = new BindingAwareModelMap();
|
||||
|
||||
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
|
||||
ModelAndView mav =
|
||||
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
|
||||
methodInvoker.updateModelAttributes(
|
||||
handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
|
||||
return mav;
|
||||
}
|
||||
catch (NoSuchRequestHandlingMethodException ex) {
|
||||
return handleNoSuchRequestHandlingMethod(ex, request, response);
|
||||
}
|
||||
catch (HttpRequestMethodNotSupportedException ex) {
|
||||
return handleHttpRequestMethodNotSupportedException(ex, request, response);
|
||||
}
|
||||
catch (HttpMediaTypeNotSupportedException ex) {
|
||||
return handleHttpMediaTypeNotSupportedException(ex, request, response);
|
||||
}
|
||||
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
|
||||
ModelAndView mav =
|
||||
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
|
||||
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
|
||||
return mav;
|
||||
}
|
||||
|
||||
public long getLastModified(HttpServletRequest request, Object handler) {
|
||||
|
|
@ -378,89 +346,25 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* Handle the case where no request handler method was found.
|
||||
* <p>The default implementation logs a warning and sends an HTTP 404 error.
|
||||
* Alternatively, a fallback view could be chosen, or the
|
||||
* NoSuchRequestHandlingMethodException could be rethrown as-is.
|
||||
* @param ex the NoSuchRequestHandlingMethodException to be handled
|
||||
* Template method for creating a new ServletRequestDataBinder instance. <p>The default implementation creates a
|
||||
* standard ServletRequestDataBinder. This can be overridden for custom ServletRequestDataBinder subclasses.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleNoSuchRequestHandlingMethod(
|
||||
NoSuchRequestHandlingMethodException ex, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
pageNotFoundLogger.warn(ex.getMessage());
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where no request handler method was found for the particular HTTP request method.
|
||||
* <p>The default implementation logs a warning, sends an HTTP 405 error and sets the "Allow" header.
|
||||
* Alternatively, a fallback view could be chosen, or the HttpRequestMethodNotSupportedException
|
||||
* could be rethrown as-is.
|
||||
* @param ex the HttpRequestMethodNotSupportedException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleHttpRequestMethodNotSupportedException(
|
||||
HttpRequestMethodNotSupportedException ex, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
pageNotFoundLogger.warn(ex.getMessage());
|
||||
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
|
||||
response.addHeader("Allow", StringUtils.arrayToDelimitedString(ex.getSupportedMethods(), ", "));
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where no {@linkplain HttpMessageConverter message converters} was found for the PUT or POSTed
|
||||
* content.
|
||||
* <p>The default implementation logs a warning, sends an HTTP 415 error and sets the "Allow" header.
|
||||
* Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotSupportedException
|
||||
* could be rethrown as-is.
|
||||
* @param ex the HttpMediaTypeNotSupportedException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception an Exception that should be thrown as result of the servlet request
|
||||
*/
|
||||
protected ModelAndView handleHttpMediaTypeNotSupportedException(
|
||||
HttpMediaTypeNotSupportedException ex, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
|
||||
response.addHeader("Accept", MediaType.toString(ex.getSupportedMediaTypes()));
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for creating a new ServletRequestDataBinder instance.
|
||||
* <p>The default implementation creates a standard ServletRequestDataBinder.
|
||||
* This can be overridden for custom ServletRequestDataBinder subclasses.
|
||||
* @param request current HTTP request
|
||||
* @param target the target object to bind onto (or <code>null</code>
|
||||
* if the binder is just used to convert a plain parameter value)
|
||||
* @param target the target object to bind onto (or <code>null</code> if the binder is just used to convert a plain
|
||||
* parameter value)
|
||||
* @param objectName the objectName of the target object
|
||||
* @return the ServletRequestDataBinder instance to use
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see ServletRequestDataBinder#bind(javax.servlet.ServletRequest)
|
||||
* @see ServletRequestDataBinder#convertIfNecessary(Object, Class, MethodParameter)
|
||||
*/
|
||||
protected ServletRequestDataBinder createBinder(
|
||||
HttpServletRequest request, Object target, String objectName) throws Exception {
|
||||
protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object target, String objectName)
|
||||
throws Exception {
|
||||
|
||||
return new ServletRequestDataBinder(target, objectName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a HandlerMethodResolver for the given handler type.
|
||||
*/
|
||||
/** Build a HandlerMethodResolver for the given handler type. */
|
||||
private ServletHandlerMethodResolver getMethodResolver(Object handler) {
|
||||
Class handlerClass = ClassUtils.getUserClass(handler);
|
||||
ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
|
||||
|
|
@ -471,10 +375,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
return resolver;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Servlet-specific subclass of {@link HandlerMethodResolver}.
|
||||
*/
|
||||
/** Servlet-specific subclass of {@link HandlerMethodResolver}. */
|
||||
private class ServletHandlerMethodResolver extends HandlerMethodResolver {
|
||||
|
||||
private ServletHandlerMethodResolver(Class<?> handlerType) {
|
||||
|
|
@ -499,7 +400,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
boolean match = false;
|
||||
if (mappingInfo.paths.length > 0) {
|
||||
List<String> matchedPaths = new ArrayList<String>(mappingInfo.paths.length);
|
||||
List<String> matchedPaths = new ArrayList<String>(mappingInfo.paths.length);
|
||||
for (String mappedPath : mappingInfo.paths) {
|
||||
if (isPathMatch(mappedPath, lookupPath)) {
|
||||
if (checkParameters(mappingInfo, request)) {
|
||||
|
|
@ -515,7 +416,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
}
|
||||
Collections.sort(matchedPaths, pathComparator);
|
||||
mappingInfo.matchedPaths = matchedPaths.toArray(new String[matchedPaths.size()]);
|
||||
mappingInfo.matchedPaths = matchedPaths.toArray(new String[matchedPaths.size()]);
|
||||
}
|
||||
else {
|
||||
// No paths specified: parameter match sufficient.
|
||||
|
|
@ -548,17 +449,19 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
}
|
||||
if (oldMappedMethod != null) {
|
||||
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
|
||||
lookupPath + "': {" + oldMappedMethod + ", " + handlerMethod +
|
||||
"}. If you intend to handle the same path in multiple methods, then factor " +
|
||||
"them out into a dedicated handler class with that path mapped at the type level!");
|
||||
throw new IllegalStateException(
|
||||
"Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +
|
||||
oldMappedMethod + ", " + handlerMethod +
|
||||
"}. If you intend to handle the same path in multiple methods, then factor " +
|
||||
"them out into a dedicated handler class with that path mapped at the type level!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!targetHandlerMethods.isEmpty()) {
|
||||
List<RequestMappingInfo> matches = new ArrayList<RequestMappingInfo>(targetHandlerMethods.keySet());
|
||||
RequestMappingInfoComparator requestMappingInfoComparator = new RequestMappingInfoComparator(pathComparator);
|
||||
RequestMappingInfoComparator requestMappingInfoComparator =
|
||||
new RequestMappingInfoComparator(pathComparator);
|
||||
Collections.sort(matches, requestMappingInfoComparator);
|
||||
RequestMappingInfo bestMappingMatch = matches.get(0);
|
||||
if (bestMappingMatch.matchedPaths.length > 0) {
|
||||
|
|
@ -597,7 +500,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void extractHandlerMethodUriTemplates(String mappedPath, String lookupPath, HttpServletRequest request) {
|
||||
private void extractHandlerMethodUriTemplates(String mappedPath,
|
||||
String lookupPath,
|
||||
HttpServletRequest request) {
|
||||
Map<String, String> variables = null;
|
||||
boolean hasSuffix = (mappedPath.indexOf('.') != -1);
|
||||
if (!hasSuffix && pathMatcher.match(mappedPath + ".*", lookupPath)) {
|
||||
|
|
@ -610,7 +515,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
String realPath = "/**/" + mappedPath;
|
||||
if (pathMatcher.match(realPath, lookupPath)) {
|
||||
variables = pathMatcher.extractUriTemplateVariables(realPath, lookupPath);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
realPath = realPath + ".*";
|
||||
if (pathMatcher.match(realPath, lookupPath)) {
|
||||
variables = pathMatcher.extractUriTemplateVariables(realPath, lookupPath);
|
||||
|
|
@ -628,17 +534,14 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Servlet-specific subclass of {@link HandlerMethodInvoker}.
|
||||
*/
|
||||
/** Servlet-specific subclass of {@link HandlerMethodInvoker}. */
|
||||
private class ServletHandlerMethodInvoker extends HandlerMethodInvoker {
|
||||
|
||||
private boolean responseArgumentUsed = false;
|
||||
|
||||
private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
||||
super(resolver, webBindingInitializer, sessionAttributeStore,
|
||||
parameterNameDiscoverer, customArgumentResolvers, messageConverters);
|
||||
super(resolver, webBindingInitializer, sessionAttributeStore, parameterNameDiscoverer,
|
||||
customArgumentResolvers, messageConverters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -655,8 +558,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
|
||||
throws Exception {
|
||||
|
||||
return AnnotationMethodHandlerAdapter.this.createBinder(
|
||||
(HttpServletRequest) webRequest.getNativeRequest(), target, objectName);
|
||||
return AnnotationMethodHandlerAdapter.this
|
||||
.createBinder((HttpServletRequest) webRequest.getNativeRequest(), target, objectName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -699,14 +602,14 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
Map<String, String> uriTemplateVariables =
|
||||
(Map<String, String>) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
if (uriTemplateVariables == null || !uriTemplateVariables.containsKey(pathVarName)) {
|
||||
throw new IllegalStateException("Could not find @PathVariable [" + pathVarName + "] in @RequestMapping");
|
||||
throw new IllegalStateException(
|
||||
"Could not find @PathVariable [" + pathVarName + "] in @RequestMapping");
|
||||
}
|
||||
return uriTemplateVariables.get(pathVarName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
|
||||
HttpServletResponse response = (HttpServletResponse) webRequest.getNativeResponse();
|
||||
|
|
@ -745,8 +648,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue,
|
||||
ExtendedModelMap implicitModel, ServletWebRequest webRequest) {
|
||||
public ModelAndView getModelAndView(Method handlerMethod,
|
||||
Class handlerType,
|
||||
Object returnValue,
|
||||
ExtendedModelMap implicitModel,
|
||||
ServletWebRequest webRequest) {
|
||||
|
||||
if (returnValue instanceof ModelAndView) {
|
||||
ModelAndView mav = (ModelAndView) returnValue;
|
||||
|
|
@ -792,7 +698,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static class RequestMappingInfo {
|
||||
|
||||
String[] paths = new String[0];
|
||||
|
|
@ -806,7 +711,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
String bestMatchedPath() {
|
||||
return matchedPaths.length > 0 ? matchedPaths[0] : null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
RequestMappingInfo other = (RequestMappingInfo) obj;
|
||||
|
|
@ -823,16 +728,12 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
|
||||
/**
|
||||
* Comparator capable of sorting {@link RequestMappingInfo}s (RHIs) so that sorting a list with this comparator will
|
||||
* result in:
|
||||
* <ul>
|
||||
* <li>RHIs with {@linkplain RequestMappingInfo#matchedPaths better matched paths} take prescedence over those with
|
||||
* a weaker match (as expressed by the {@linkplain PathMatcher#getPatternComparator(String) path pattern
|
||||
* comparator}.) Typically, this means that patterns without wild chards and uri templates will be ordered before those without.</li>
|
||||
* <li>RHIs with one single {@linkplain RequestMappingInfo#methods request method} will be ordered before those
|
||||
* without a method, or with more than one method.</li>
|
||||
* <li>RHIs with more {@linkplain RequestMappingInfo#params request parameters} will be ordered before those with
|
||||
* less parameters</li>
|
||||
* </ol>
|
||||
* result in: <ul> <li>RHIs with {@linkplain RequestMappingInfo#matchedPaths better matched paths} take prescedence
|
||||
* over those with a weaker match (as expressed by the {@linkplain PathMatcher#getPatternComparator(String) path
|
||||
* pattern comparator}.) Typically, this means that patterns without wild chards and uri templates will be ordered
|
||||
* before those without.</li> <li>RHIs with one single {@linkplain RequestMappingInfo#methods request method} will be
|
||||
* ordered before those without a method, or with more than one method.</li> <li>RHIs with more {@linkplain
|
||||
* RequestMappingInfo#params request parameters} will be ordered before those with less parameters</li> </ol>
|
||||
*/
|
||||
static class RequestMappingInfoComparator implements Comparator<RequestMappingInfo> {
|
||||
|
||||
|
|
@ -867,5 +768,5 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
return (info1ParamCount < info2ParamCount ? 1 : (info1ParamCount == info2ParamCount ? 0 : -1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
public class DefaultHandlerExceptionResolverTests {
|
||||
|
||||
private DefaultHandlerExceptionResolver exceptionResolver;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
exceptionResolver = new DefaultHandlerExceptionResolver();
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
request.setMethod("GET");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleNoSuchRequestHandlingMethod() {
|
||||
NoSuchRequestHandlingMethodException ex = new NoSuchRequestHandlingMethodException(request);
|
||||
exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertEquals("Invalid status code", 404, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleHttpRequestMethodNotSupported() {
|
||||
HttpRequestMethodNotSupportedException ex =
|
||||
new HttpRequestMethodNotSupportedException("GET", new String[]{"POST", "PUT"});
|
||||
exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertEquals("Invalid status code", 405, response.getStatus());
|
||||
assertEquals("Invalid Allow header", "POST, PUT", response.getHeader("Allow"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleHttpMediaTypeNotSupported() {
|
||||
HttpMediaTypeNotSupportedException ex = new HttpMediaTypeNotSupportedException(new MediaType("text", "plain"),
|
||||
Collections.singletonList(new MediaType("application", "pdf")));
|
||||
exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertEquals("Invalid status code", 415, response.getStatus());
|
||||
assertEquals("Invalid Accept header", "application/pdf", response.getHeader("Accept"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleMissingServletRequestParameter() {
|
||||
MissingServletRequestParameterException ex = new MissingServletRequestParameterException("foo", "bar");
|
||||
exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertEquals("Invalid status code", 400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleTypeMismatch() {
|
||||
TypeMismatchException ex = new TypeMismatchException("foo", String.class);
|
||||
exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertEquals("Invalid status code", 400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleHttpMessageNotReadable() {
|
||||
HttpMessageNotReadableException ex = new HttpMessageNotReadableException("foo");
|
||||
exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertEquals("Invalid status code", 400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleHttpMessageNotWritable() {
|
||||
HttpMessageNotWritableException ex = new HttpMessageNotWritableException("foo");
|
||||
exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertEquals("Invalid status code", 500, response.getStatus());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import java.io.Writer;
|
|||
import java.security.Principal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
|
@ -51,6 +52,12 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
|||
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletConfig;
|
||||
|
|
@ -62,7 +69,6 @@ import org.springframework.ui.ModelMap;
|
|||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
|
|
@ -107,13 +113,25 @@ public class ServletAnnotationControllerTests {
|
|||
assertEquals("test", response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test(expected = MissingServletRequestParameterException.class)
|
||||
@Test
|
||||
public void requiredParamMissing() throws Exception {
|
||||
initServlet(RequiredParamController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeConversionError() throws Exception {
|
||||
initServlet(RequiredParamController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||
request.addParameter("id", "foo");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -157,7 +175,7 @@ public class ServletAnnotationControllerTests {
|
|||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("Invalid response status", HttpServletResponse.SC_METHOD_NOT_ALLOWED, response.getStatus());
|
||||
String allowHeader = (String)response.getHeader("Allow");
|
||||
String allowHeader = (String) response.getHeader("Allow");
|
||||
assertNotNull("No Allow header", allowHeader);
|
||||
Set<String> allowedMethods = new HashSet<String>();
|
||||
allowedMethods.addAll(Arrays.asList(StringUtils.delimitedListToStringArray(allowHeader, ", ")));
|
||||
|
|
@ -249,7 +267,6 @@ public class ServletAnnotationControllerTests {
|
|||
servlet.init(new MockServletConfig());
|
||||
}
|
||||
|
||||
|
||||
private void doTestAdaptedHandleMethods(final Class<?> controllerClass) throws Exception {
|
||||
initServlet(controllerClass);
|
||||
|
||||
|
|
@ -878,6 +895,31 @@ public class ServletAnnotationControllerTests {
|
|||
assertNotNull("No Accept response header set", response.getHeader("Accept"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void badRequestRequestBody() throws ServletException, IOException {
|
||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
||||
@Override
|
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(RequestBodyController.class));
|
||||
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
||||
adapterDef.getPropertyValues().addPropertyValue("messageConverters", new MyMessageConverter());
|
||||
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
||||
wac.refresh();
|
||||
return wac;
|
||||
}
|
||||
};
|
||||
servlet.init(new MockServletConfig());
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/something");
|
||||
String requestBody = "Hello World";
|
||||
request.setContent(requestBody.getBytes("UTF-8"));
|
||||
request.addHeader("Content-Type", "application/pdf");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
* Controllers
|
||||
*/
|
||||
|
|
@ -893,8 +935,7 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/** @noinspection UnusedDeclaration*/
|
||||
/** @noinspection UnusedDeclaration */
|
||||
private static class BaseController {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
|
|
@ -903,7 +944,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class MyAdaptedController {
|
||||
|
||||
|
|
@ -913,8 +953,10 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@RequestMapping("/myPath2.do")
|
||||
public void myHandle(@RequestParam("param1") String p1, @RequestParam("param2") int p2,
|
||||
@RequestHeader("header1") long h1, @CookieValue("cookie1") Cookie c1,
|
||||
public void myHandle(@RequestParam("param1") String p1,
|
||||
@RequestParam("param2") int p2,
|
||||
@RequestHeader("header1") long h1,
|
||||
@CookieValue("cookie1") Cookie c1,
|
||||
HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write("test-" + p1 + "-" + p2 + "-" + h1 + "-" + c1.getValue());
|
||||
}
|
||||
|
|
@ -930,7 +972,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/*.do")
|
||||
private static class MyAdaptedController2 {
|
||||
|
|
@ -941,8 +982,11 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@RequestMapping("/myPath2.do")
|
||||
public void myHandle(@RequestParam("param1") String p1, int param2, HttpServletResponse response,
|
||||
@RequestHeader("header1") String h1, @CookieValue("cookie1") String c1) throws IOException {
|
||||
public void myHandle(@RequestParam("param1") String p1,
|
||||
int param2,
|
||||
HttpServletResponse response,
|
||||
@RequestHeader("header1") String h1,
|
||||
@CookieValue("cookie1") String c1) throws IOException {
|
||||
response.getWriter().write("test-" + p1 + "-" + param2 + "-" + h1 + "-" + c1);
|
||||
}
|
||||
|
||||
|
|
@ -957,13 +1001,15 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class MyAdaptedControllerBase<T> {
|
||||
|
||||
@RequestMapping("/myPath2.do")
|
||||
public void myHandle(@RequestParam("param1") T p1, int param2, @RequestHeader Integer header1,
|
||||
@CookieValue int cookie1, HttpServletResponse response) throws IOException {
|
||||
public void myHandle(@RequestParam("param1") T p1,
|
||||
int param2,
|
||||
@RequestHeader Integer header1,
|
||||
@CookieValue int cookie1,
|
||||
HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write("test-" + p1 + "-" + param2 + "-" + header1 + "-" + cookie1);
|
||||
}
|
||||
|
||||
|
|
@ -976,7 +1022,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping("/*.do")
|
||||
private static class MyAdaptedController3 extends MyAdaptedControllerBase<String> {
|
||||
|
||||
|
|
@ -986,8 +1031,11 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void myHandle(@RequestParam("param1") String p1, int param2, @RequestHeader Integer header1,
|
||||
@CookieValue int cookie1, HttpServletResponse response) throws IOException {
|
||||
public void myHandle(@RequestParam("param1") String p1,
|
||||
int param2,
|
||||
@RequestHeader Integer header1,
|
||||
@CookieValue int cookie1,
|
||||
HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write("test-" + p1 + "-" + param2 + "-" + header1 + "-" + cookie1);
|
||||
}
|
||||
|
||||
|
|
@ -1012,7 +1060,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
private static class EmptyParameterListHandlerMethodController {
|
||||
|
|
@ -1029,7 +1076,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
public static class MyFormController {
|
||||
|
||||
|
|
@ -1042,7 +1088,7 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@RequestMapping("/myPath.do")
|
||||
public String myHandle(@ModelAttribute("myCommand")TestBean tb, BindingResult errors, ModelMap model) {
|
||||
public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, ModelMap model) {
|
||||
if (!model.containsKey("myKey")) {
|
||||
model.addAttribute("myKey", "myValue");
|
||||
}
|
||||
|
|
@ -1050,7 +1096,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
public static class MyModelFormController {
|
||||
|
||||
|
|
@ -1063,7 +1108,7 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@RequestMapping("/myPath.do")
|
||||
public String myHandle(@ModelAttribute("myCommand")TestBean tb, BindingResult errors, Model model) {
|
||||
public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, Model model) {
|
||||
if (!model.containsAttribute("myKey")) {
|
||||
model.addAttribute("myKey", "myValue");
|
||||
}
|
||||
|
|
@ -1071,13 +1116,13 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class MyCommandProvidingFormController<T, TB, TB2> extends MyFormController {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ModelAttribute("myCommand")
|
||||
private TestBean createTestBean(@RequestParam T defaultName, Map<String, Object> model,
|
||||
private TestBean createTestBean(@RequestParam T defaultName,
|
||||
Map<String, Object> model,
|
||||
@RequestParam Date date) {
|
||||
model.put("myKey", "myOriginalValue");
|
||||
return new TestBean(defaultName.getClass().getSimpleName() + ":" + defaultName.toString());
|
||||
|
|
@ -1085,7 +1130,7 @@ public class ServletAnnotationControllerTests {
|
|||
|
||||
@Override
|
||||
@RequestMapping("/myPath.do")
|
||||
public String myHandle(@ModelAttribute("myCommand")TestBean tb, BindingResult errors, ModelMap model) {
|
||||
public String myHandle(@ModelAttribute("myCommand") TestBean tb, BindingResult errors, ModelMap model) {
|
||||
return super.myHandle(tb, errors, model);
|
||||
}
|
||||
|
||||
|
|
@ -1110,21 +1155,18 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MySpecialArg {
|
||||
|
||||
public MySpecialArg(String value) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class MyTypedCommandProvidingFormController
|
||||
extends MyCommandProvidingFormController<Integer, TestBean, ITestBean> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class MyBinderInitializingCommandProvidingFormController extends MyCommandProvidingFormController {
|
||||
|
||||
|
|
@ -1138,7 +1180,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class MySpecificBinderInitializingCommandProvidingFormController
|
||||
extends MyCommandProvidingFormController {
|
||||
|
|
@ -1155,7 +1196,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MyWebBindingInitializer implements WebBindingInitializer {
|
||||
|
||||
public void initBinder(WebDataBinder binder, WebRequest request) {
|
||||
|
|
@ -1166,7 +1206,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MySpecialArgumentResolver implements WebArgumentResolver {
|
||||
|
||||
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
|
||||
|
|
@ -1177,7 +1216,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/myPath.do")
|
||||
private static class MyParameterDispatchingController {
|
||||
|
|
@ -1223,7 +1261,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping(value = "/myPath.do", params = {"active"})
|
||||
private static class MyConstrainedParameterDispatchingController {
|
||||
|
|
@ -1239,14 +1276,12 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping(value = "/*.do", method = RequestMethod.POST, params = "myParam=myValue")
|
||||
private static class MyPostMethodNameDispatchingController extends MethodNameDispatchingController {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/myApp/*")
|
||||
private static class MyRelativePathDispatchingController {
|
||||
|
|
@ -1272,7 +1307,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class MyNullCommandController {
|
||||
|
||||
|
|
@ -1287,8 +1321,11 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@RequestMapping("/myPath")
|
||||
public void handle(@ModelAttribute TestBean testBean, Errors errors, @ModelAttribute TestPrincipal modelPrinc,
|
||||
OtherPrincipal requestPrinc, Writer writer) throws IOException {
|
||||
public void handle(@ModelAttribute TestBean testBean,
|
||||
Errors errors,
|
||||
@ModelAttribute TestPrincipal modelPrinc,
|
||||
OtherPrincipal requestPrinc,
|
||||
Writer writer) throws IOException {
|
||||
assertNull(testBean);
|
||||
assertNotNull(modelPrinc);
|
||||
assertNotNull(requestPrinc);
|
||||
|
|
@ -1298,7 +1335,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static class TestPrincipal implements Principal {
|
||||
|
||||
public String getName() {
|
||||
|
|
@ -1306,7 +1342,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static class OtherPrincipal implements Principal {
|
||||
|
||||
public String getName() {
|
||||
|
|
@ -1314,7 +1349,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static class TestViewResolver implements ViewResolver {
|
||||
|
||||
public View resolveViewName(final String viewName, Locale locale) throws Exception {
|
||||
|
|
@ -1345,9 +1379,9 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
List<TestBean> testBeans = (List<TestBean>) model.get("testBeanList");
|
||||
if (errors.hasFieldErrors("age")) {
|
||||
response.getWriter().write(viewName + "-" + tb.getName() + "-" +
|
||||
errors.getFieldError("age").getCode() + "-" + testBeans.get(0).getName() + "-" +
|
||||
model.get("myKey"));
|
||||
response.getWriter()
|
||||
.write(viewName + "-" + tb.getName() + "-" + errors.getFieldError("age").getCode() +
|
||||
"-" + testBeans.get(0).getName() + "-" + model.get("myKey"));
|
||||
}
|
||||
else {
|
||||
response.getWriter().write(viewName + "-" + tb.getName() + "-" + tb.getAge() + "-" +
|
||||
|
|
@ -1358,7 +1392,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ParentController {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
|
|
@ -1366,7 +1399,6 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/child/test")
|
||||
public static class ChildController extends ParentController {
|
||||
|
|
@ -1376,68 +1408,66 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
public static class RequiredParamController {
|
||||
|
||||
@RequestMapping("/myPath.do")
|
||||
public void myHandle(@RequestParam(value = "id", required = true) String id,
|
||||
public void myHandle(@RequestParam(value = "id", required = true) int id,
|
||||
@RequestHeader(value = "header", required = true) String header) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
public static class OptionalParamController {
|
||||
|
||||
@RequestMapping("/myPath.do")
|
||||
public void myHandle(@RequestParam(required = false) String id, @RequestParam(required = false) boolean flag,
|
||||
@RequestHeader(value = "header", required = false) String header, HttpServletResponse response)
|
||||
throws IOException {
|
||||
public void myHandle(@RequestParam(required = false) String id,
|
||||
@RequestParam(required = false) boolean flag,
|
||||
@RequestHeader(value = "header", required = false) String header,
|
||||
HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write(String.valueOf(id) + "-" + flag + "-" + String.valueOf(header));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
public static class DefaultValueParamController {
|
||||
|
||||
@RequestMapping("/myPath.do")
|
||||
public void myHandle(@RequestParam(value = "id", defaultValue = "foo") String id,
|
||||
@RequestHeader(defaultValue = "bar") String header, HttpServletResponse response)
|
||||
throws IOException {
|
||||
@RequestHeader(defaultValue = "bar") String header,
|
||||
HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write(String.valueOf(id) + "-" + String.valueOf(header));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
public static class MethodNotAllowedController {
|
||||
|
||||
@RequestMapping(value="/myPath.do", method = RequestMethod.DELETE)
|
||||
@RequestMapping(value = "/myPath.do", method = RequestMethod.DELETE)
|
||||
public void delete() {
|
||||
}
|
||||
|
||||
@RequestMapping(value="/myPath.do", method = RequestMethod.HEAD)
|
||||
@RequestMapping(value = "/myPath.do", method = RequestMethod.HEAD)
|
||||
public void head() {
|
||||
}
|
||||
|
||||
@RequestMapping(value="/myPath.do", method = RequestMethod.OPTIONS)
|
||||
@RequestMapping(value = "/myPath.do", method = RequestMethod.OPTIONS)
|
||||
public void options() {
|
||||
}
|
||||
@RequestMapping(value="/myPath.do", method = RequestMethod.POST)
|
||||
|
||||
@RequestMapping(value = "/myPath.do", method = RequestMethod.POST)
|
||||
public void post() {
|
||||
}
|
||||
|
||||
@RequestMapping(value="/myPath.do", method = RequestMethod.PUT)
|
||||
@RequestMapping(value = "/myPath.do", method = RequestMethod.PUT)
|
||||
public void put() {
|
||||
}
|
||||
|
||||
@RequestMapping(value="/myPath.do", method = RequestMethod.TRACE)
|
||||
@RequestMapping(value = "/myPath.do", method = RequestMethod.TRACE)
|
||||
public void trace() {
|
||||
}
|
||||
|
||||
@RequestMapping(value="/otherPath.do", method = RequestMethod.GET)
|
||||
@RequestMapping(value = "/otherPath.do", method = RequestMethod.GET)
|
||||
public void get() {
|
||||
}
|
||||
}
|
||||
|
|
@ -1445,7 +1475,6 @@ public class ServletAnnotationControllerTests {
|
|||
@Controller
|
||||
public static class PathOrderingController {
|
||||
|
||||
|
||||
@RequestMapping(value = {"/dir/myPath1.do", "/**/*.do"})
|
||||
public void method1(Writer writer) throws IOException {
|
||||
writer.write("method1");
|
||||
|
|
@ -1466,4 +1495,26 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
public static class MyMessageConverter implements HttpMessageConverter {
|
||||
|
||||
public boolean supports(Class clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public List getSupportedMediaTypes() {
|
||||
return Collections.singletonList(new MediaType("application", "pdf"));
|
||||
}
|
||||
|
||||
public Object read(Class clazz, HttpInputMessage inputMessage)
|
||||
throws IOException, HttpMessageNotReadableException {
|
||||
throw new HttpMessageNotReadableException("Could not read");
|
||||
}
|
||||
|
||||
public void write(Object o, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
throw new UnsupportedOperationException("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import java.io.Writer;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
|
@ -24,9 +25,7 @@ import org.springframework.web.context.WebApplicationContext;
|
|||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
/** @author Arjen Poutsma */
|
||||
public class UriTemplateServletAnnotationControllerTests {
|
||||
|
||||
private DispatcherServlet servlet;
|
||||
|
|
@ -92,6 +91,16 @@ public class UriTemplateServletAnnotationControllerTests {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeConversionError() throws Exception {
|
||||
initServlet(SimpleUriTemplateController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo.xml");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
|
||||
}
|
||||
|
||||
private void initServlet(final Class<?> controllerclass) throws ServletException {
|
||||
servlet = new DispatcherServlet() {
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Reference in New Issue