Spring MVC : 工具 DefaultHandlerExceptionResolver

DefaultHandlerExceptionResolverSpring MVC对接口HandlerExceptionResolver的一个内置实现,这个HandlerExceptionResolver,顾名思义,是作为"缺省"HandlerExceptionResolver被使用的,也就是说,如果其他HandlerExceptionResolver不能处理指定的异常,最后会使用DefaultHandlerExceptionResolver来处理。而DefaultHandlerExceptionResolver能够解析标准Spring MVC异常,将其翻译成相应的HTTP状态码,通过response.sendError方法处理响应。以下是DefaultHandlerExceptionResolver处理的异常和相应的HTTP状态字的对照表 :

Spring MVC 标准异常HTTP状态码介绍
HttpRequestMethodNotSupportedException405 (SC_METHOD_NOT_ALLOWED)HTTP方法不支持
HttpMediaTypeNotSupportedException415 (SC_UNSUPPORTED_MEDIA_TYPE)MIME类型不支持
HttpMediaTypeNotAcceptableException406 (SC_NOT_ACCEPTABLE)MIME类型不被接受
MissingPathVariableException500 (SC_INTERNAL_SERVER_ERROR)路径参数缺失
MissingServletRequestParameterException400 (SC_BAD_REQUEST)Servlet请求参数缺失
ServletRequestBindingException400 (SC_BAD_REQUEST)Servlet请求处理过程中的绑定异常
ConversionNotSupportedException500 (SC_INTERNAL_SERVER_ERROR)不支持该类型转换
TypeMismatchException400 (SC_BAD_REQUEST)类型不匹配
HttpMessageNotReadableException400 (SC_BAD_REQUEST)HTTP消息不可读
使用者:HttpMessageConverter#read
HttpMessageNotWritableException500 (SC_INTERNAL_SERVER_ERROR)HTTP消息不可写
使用者:HttpMessageConverter#write
MethodArgumentNotValidException400 (SC_BAD_REQUEST)方法参数值验证失败
MissingServletRequestPartException400 (SC_BAD_REQUEST)multipart/form-data请求时,
part名称指定的part数据不存在
BindException400 (SC_BAD_REQUEST)Spring验证框架中的绑定异常
NoHandlerFoundException404 (SC_NOT_FOUND)没有找到能处理该请求的handler
AsyncRequestTimeoutException503 (SC_SERVICE_UNAVAILABLE)异步请求超时

上表中提到的异常之间的关系如下 :
在这里插入图片描述

异常解析器DefaultHandlerExceptionResolverSpring MVC的前端控制器DispatcherServlet缺省启用的HandlerExceptionResolver之一,并且被设置为最低优先级。

DefaultHandlerExceptionResolverSpring 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);
	}

}

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值