DefaultHandlerExceptionResolver
是Spring MVC
对接口HandlerExceptionResolver
的一个内置实现,这个HandlerExceptionResolver
,顾名思义,是作为"缺省"HandlerExceptionResolver
被使用的,也就是说,如果其他HandlerExceptionResolver
不能处理指定的异常,最后会使用DefaultHandlerExceptionResolver
来处理。而DefaultHandlerExceptionResolver
能够解析标准Spring MVC
异常,将其翻译成相应的HTTP
状态码,通过response.sendError
方法处理响应。以下是DefaultHandlerExceptionResolver
处理的异常和相应的HTTP
状态字的对照表 :
Spring MVC 标准异常 | HTTP状态码 | 介绍 |
---|---|---|
HttpRequestMethodNotSupportedException | 405 (SC_METHOD_NOT_ALLOWED ) | HTTP 方法不支持 |
HttpMediaTypeNotSupportedException | 415 (SC_UNSUPPORTED_MEDIA_TYPE ) | MIME 类型不支持 |
HttpMediaTypeNotAcceptableException | 406 (SC_NOT_ACCEPTABLE ) | MIME 类型不被接受 |
MissingPathVariableException | 500 (SC_INTERNAL_SERVER_ERROR ) | 路径参数缺失 |
MissingServletRequestParameterException | 400 (SC_BAD_REQUEST ) | Servlet 请求参数缺失 |
ServletRequestBindingException | 400 (SC_BAD_REQUEST ) | Servlet 请求处理过程中的绑定异常 |
ConversionNotSupportedException | 500 (SC_INTERNAL_SERVER_ERROR ) | 不支持该类型转换 |
TypeMismatchException | 400 (SC_BAD_REQUEST ) | 类型不匹配 |
HttpMessageNotReadableException | 400 (SC_BAD_REQUEST ) | HTTP 消息不可读使用者: HttpMessageConverter#read |
HttpMessageNotWritableException | 500 (SC_INTERNAL_SERVER_ERROR ) | HTTP 消息不可写使用者: HttpMessageConverter#write |
MethodArgumentNotValidException | 400 (SC_BAD_REQUEST ) | 方法参数值验证失败 |
MissingServletRequestPartException | 400 (SC_BAD_REQUEST ) | multipart/form-data 请求时,part 名称指定的part 数据不存在 |
BindException | 400 (SC_BAD_REQUEST ) | Spring 验证框架中的绑定异常 |
NoHandlerFoundException | 404 (SC_NOT_FOUND ) | 没有找到能处理该请求的handler |
AsyncRequestTimeoutException | 503 (SC_SERVICE_UNAVAILABLE ) | 异步请求超时 |
上表中提到的异常之间的关系如下 :
异常解析器DefaultHandlerExceptionResolver
是Spring MVC
的前端控制器DispatcherServlet
缺省启用的HandlerExceptionResolver
之一,并且被设置为最低优先级。
DefaultHandlerExceptionResolver
是Spring MVC
配置机制WebMvcConfigurationSupport
缺省定义的一个HandlerExceptionResolver
。
DefaultHandlerExceptionResolver
的核心方法是#doResolveException
,该方法内部根据异常的类型调用不同的方法将异常翻译成相应的HTTP
状态码并sendError
到响应对象,并且返回值是一个空ModelAdnView
对象,也就是一个其#isEmpty
方法总是为true
的对象,此返回值告诉调用者:请使用缺省视图渲染相应的错误信息。
源代码解析
package org.springframework.web.servlet.mvc.support;
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 #setOrder(int) order to #LOWEST_PRECEDENCE.
*/
public DefaultHandlerExceptionResolver() {
// 注意该构造函数中将该异常解析器的优先级设置为最低
setOrder(Ordered.LOWEST_PRECEDENCE);
setWarnLogCategory(getClass().getName());
}
// 根据参数 ex 异常对象的类型解析参数,将其转换成一个 ModelAndView 对象返回,
// 并且注意该过程中涉及到对 response 头部的设置,已经对其调用 sendError() .
// 如果参数 ex 不能被当前异常处理器处理,返回 null
// 该方法对基类定义的该方法提供了实现,该方法会被基类公开方法 resolveException() 使用,
// resolveException() 是接口 HandlerExceptionResolver 所定义的方法
@Override
@Nullable
protected ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler,
Exception ex) {
try {
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 HttpMediaTypeNotAcceptableException) {
return handleHttpMediaTypeNotAcceptable(
(HttpMediaTypeNotAcceptableException) ex, request, response, handler);
}
else if (ex instanceof MissingPathVariableException) {
return handleMissingPathVariable(
(MissingPathVariableException) ex, request, response, handler);
}
else if (ex instanceof MissingServletRequestParameterException) {
return handleMissingServletRequestParameter(
(MissingServletRequestParameterException) ex, request, response, handler);
}
else if (ex instanceof ServletRequestBindingException) {
return handleServletRequestBindingException(
(ServletRequestBindingException) ex, request, response, handler);
}
else if (ex instanceof ConversionNotSupportedException) {
return handleConversionNotSupported(
(ConversionNotSupportedException) 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);
}
else if (ex instanceof MethodArgumentNotValidException) {
return handleMethodArgumentNotValidException(
(MethodArgumentNotValidException) ex, request, response, handler);
}
else if (ex instanceof MissingServletRequestPartException) {
return handleMissingServletRequestPartException(
(MissingServletRequestPartException) ex, request, response, handler);
}
else if (ex instanceof BindException) {
return handleBindException((BindException) ex, request, response, handler);
}
else if (ex instanceof NoHandlerFoundException) {
return handleNoHandlerFoundException(
(NoHandlerFoundException) ex, request, response, handler);
}
else if (ex instanceof AsyncRequestTimeoutException) {
return handleAsyncRequestTimeoutException(
(AsyncRequestTimeoutException) ex, request, response, handler);
}
}
catch (Exception handlerEx) {
if (logger.isWarnEnabled()) {
logger.warn("Failure while trying to resolve exception ["
+ ex.getClass().getName() + "]", handlerEx);
}
}
return null;
}
/**
* Handle the case where no request handler method was found for the particular HTTP request
* method.
* The default implementation logs a warning, sends an HTTP 405 error, sets the "Allow" header,
* and returns an empty 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 null if none chosen
* at the time of the exception (for example, if multipart resolution failed)
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleHttpRequestMethodNotSupported(
HttpRequestMethodNotSupportedException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
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 org.springframework.http.converter.HttpMessageConverter message
* converters were found for the PUT or POSTed content.
* The default implementation sends an HTTP 415 error, sets the "Accept" header,
* and returns an empty 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
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
List<MediaType> mediaTypes = ex.getSupportedMediaTypes();
if (!CollectionUtils.isEmpty(mediaTypes)) {
response.setHeader("Accept", MediaType.toString(mediaTypes));
}
return new ModelAndView();
}
/**
* Handle the case where no org.springframework.http.converter.HttpMessageConverter message
* converters
* were found that were acceptable for the client (expressed via the Accept header.
* The default implementation sends an HTTP 406 error and returns an empty ModelAndView.
* Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotAcceptableException
* could be rethrown as-is.
* @param ex the HttpMediaTypeNotAcceptableException to be handled
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
return new ModelAndView();
}
/**
* Handle the case when a declared path variable does not match any extracted URI variable.
* The default implementation sends an HTTP 500 error, and returns an empty ModelAndView.
* Alternatively, a fallback view could be chosen, or the MissingPathVariableException
* could be rethrown as-is.
* @param ex the MissingPathVariableException to be handled
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
* @since 4.2
*/
protected ModelAndView handleMissingPathVariable(MissingPathVariableException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage());
return new ModelAndView();
}
/**
* Handle the case when a required parameter is missing.
* The default implementation sends an HTTP 400 error, and returns an empty 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
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleMissingServletRequestParameter(
MissingServletRequestParameterException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
return new ModelAndView();
}
/**
* Handle the case when an unrecoverable binding exception occurs - e.g. required header,
* required cookie.
* The default implementation sends an HTTP 400 error, and returns an empty ModelAndView.
* Alternatively, a fallback view could be chosen, or the exception could be rethrown as-is.
* @param ex the exception to be handled
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleServletRequestBindingException(ServletRequestBindingException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
return new ModelAndView();
}
/**
* Handle the case when a org.springframework.web.bind.WebDataBinder conversion cannot occur.
* The default implementation sends an HTTP 500 error, and returns an empty ModelAndView.
* Alternatively, a fallback view could be chosen, or the ConversionNotSupportedException could be
* rethrown as-is.
* @param ex the ConversionNotSupportedException to be handled
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleConversionNotSupported(ConversionNotSupportedException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
sendServerError(ex, request, response);
return new ModelAndView();
}
/**
* Handle the case when a org.springframework.web.bind.WebDataBinder conversion error occurs.
* The default implementation sends an HTTP 400 error, and returns an empty 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
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleTypeMismatch(TypeMismatchException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return new ModelAndView();
}
/**
* Handle the case where a org.springframework.http.converter.HttpMessageConverter message
* converter cannot read from a HTTP request.
* The default implementation sends an HTTP 400 error, and returns an empty ModelAndView.
* Alternatively, a fallback view could be chosen, or the HttpMessageNotReadableException 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
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return new ModelAndView();
}
/**
* Handle the case where a
* org.springframework.http.converter.HttpMessageConverter message converter
* cannot write to a HTTP request.
* The default implementation sends an HTTP 500 error, and returns an empty ModelAndView.
* Alternatively, a fallback view could be chosen, or the HttpMessageNotWritableException 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
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleHttpMessageNotWritable(HttpMessageNotWritableException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
sendServerError(ex, request, response);
return new ModelAndView();
}
/**
* Handle the case where an argument annotated with @Valid such as
* an RequestBody or RequestPart argument fails validation.
* By default, an HTTP 400 error is sent back to the client.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleMethodArgumentNotValidException(MethodArgumentNotValidException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return new ModelAndView();
}
/**
* Handle the case where an RequestPart @RequestPart, a MultipartFile,
* or a javax.servlet.http.Part argument is required but is missing.
* By default, an HTTP 400 error is sent back to the client.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleMissingServletRequestPartException(
MissingServletRequestPartException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
return new ModelAndView();
}
/**
* Handle the case where an ModelAttribute @ModelAttribute method
* argument has binding or validation errors and is not followed by another
* method argument of type BindingResult.
* By default, an HTTP 400 error is sent back to the client.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
*/
protected ModelAndView handleBindException(BindException ex, HttpServletRequest request,
HttpServletResponse response, @Nullable Object handler) throws IOException {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return new ModelAndView();
}
/**
* Handle the case where no handler was found during the dispatch.
* The default implementation sends an HTTP 404 error and returns an empty
* ModelAndView. Alternatively, a fallback view could be chosen,
* or the NoHandlerFoundException could be rethrown as-is.
* @param ex the NoHandlerFoundException to be handled
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or null if none chosen
* at the time of the exception (for example, if multipart resolution failed)
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
* @since 4.0
*/
protected ModelAndView handleNoHandlerFoundException(NoHandlerFoundException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
pageNotFoundLogger.warn(ex.getMessage());
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return new ModelAndView();
}
/**
* Handle the case where an async request timed out.
* The default implementation sends an HTTP 503 error.
* @param ex the AsyncRequestTimeoutException to be handled
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or null if none chosen
* at the time of the exception (for example, if multipart resolution failed)
* @return an empty ModelAndView indicating the exception was handled
* @throws IOException potentially thrown from HttpServletResponse#sendError
* @since 4.2.8
*/
protected ModelAndView handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler)
throws IOException {
if (!response.isCommitted()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
else {
logger.warn("Async request timed out");
}
return new ModelAndView();
}
/**
* Invoked to send a server error. Sets the status to 500 and also sets the
* request attribute "javax.servlet.error.exception" to the Exception.
*/
protected void sendServerError(Exception ex, HttpServletRequest request,
HttpServletResponse response) throws IOException {
request.setAttribute("javax.servlet.error.exception", ex);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
参考文章
- Spring MVC 概念模型 – 接口 HandlerExceptionResolver
- Spring MVC : 工具 ExceptionHandlerExceptionResolver
- Spring MVC : 工具 ResponseStatusExceptionResolver
- Spring MVC : 工具 SimpleMappingExceptionResolver
- Spring MVC : WebMvcConfigurationSupport 中定义的 HandlerExceptionResolver 组件
- How Spring Boot Initializes the Spring MVC Application Context