All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.buession.web.servlet.AbstractHandlerExceptionResolver Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.
 * See the NOTICE file distributed with this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you 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.
 *
 * =========================================================================================================
 *
 * This software consists of voluntary contributions made by many individuals on behalf of the
 * Apache Software Foundation. For more information on the Apache Software Foundation, please see
 * .
 *
 * +-------------------------------------------------------------------------------------------------------+
 * | License: http://www.apache.org/licenses/LICENSE-2.0.txt 										       |
 * | Author: Yong.Teng  													       |
 * | Copyright @ 2013-2023 Buession.com Inc.														       |
 * +-------------------------------------------------------------------------------------------------------+
 */
package com.buession.web.servlet;

import com.buession.core.collect.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.springframework.web.util.WebUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author Yong.Teng
 */
public abstract class AbstractHandlerExceptionResolver
		extends org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver
		implements ServletExceptionHandlerResolver {

	/**
	 * 异常属性名称
	 */
	private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;

	/**
	 * 默认错误视图
	 */
	private String defaultErrorView = DEFAULT_ERROR_VIEW;

	private String cacheControl = CACHE_CONTROL;

	private Map errorViews;

	private Map exceptionViews;

	protected final Logger logger = LoggerFactory.getLogger(getClass());

	protected final static Logger pageNotFoundLogger = LoggerFactory.getLogger(PAGE_NOT_FOUND_LOG_CATEGORY);

	/**
	 * 返回异常属性名称
	 *
	 * @return 异常属性名称
	 *
	 * @since 2.2.1
	 */
	public String getExceptionAttribute() {
		return exceptionAttribute;
	}

	/**
	 * 设置异常属性名称
	 *
	 * @param exceptionAttribute
	 * 		异常属性名称
	 *
	 * @since 2.2.1
	 */
	public void setExceptionAttribute(String exceptionAttribute) {
		this.exceptionAttribute = exceptionAttribute;
	}

	/**
	 * 返回默认错误视图
	 *
	 * @return 默认错误视图
	 *
	 * @since 2.2.1
	 */
	public String getDefaultErrorView() {
		return defaultErrorView;
	}

	/**
	 * 设置默认错误视图
	 *
	 * @param defaultErrorView
	 * 		默认错误视图
	 *
	 * @since 2.2.1
	 */
	public void setDefaultErrorView(String defaultErrorView) {
		this.defaultErrorView = defaultErrorView;
	}

	public String getCacheControl() {
		return cacheControl;
	}

	public void setCacheControl(String cacheControl) {
		this.cacheControl = cacheControl;
	}

	public Map getErrorViews() {
		return errorViews;
	}

	public void setErrorViews(Map errorViews) {
		this.errorViews = errorViews;
	}

	public Map getExceptionViews() {
		return exceptionViews;
	}

	public void setExceptionViews(Map exceptionViews) {
		this.exceptionViews = exceptionViews;
	}

	@ExceptionHandler(value = {Throwable.class, Exception.class})
	@Nullable
	@Override
	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
											  @Nullable Object handler, Exception ex) {
		if(logger.isErrorEnabled()){
			logger.error("Resolve Exception: {}", ex.getMessage(), ex);
		}
		ModelAndView mv = doSpecialResolveException(request, response, handler, ex);

		if(mv != null){
			return mv;
		}

		try{
			if(ex instanceof MethodArgumentNotValidException){
				return handleMethodArgumentNotValidException(request, response, handler,
						(MethodArgumentNotValidException) ex);
			}else if(ex instanceof MissingServletRequestPartException){
				return handleMissingServletRequestPartException(request, response, handler,
						(MissingServletRequestPartException) ex);
			}else if(ex instanceof BindException){
				return handleBindException(request, response, handler, (BindException) ex);
			}else if(ex instanceof TypeMismatchException){
				return handleTypeMismatchException(request, response, handler, (TypeMismatchException) ex);
			}else if(ex instanceof HttpMessageNotReadableException){
				return handleHttpMessageNotReadableException(request, response, handler,
						(HttpMessageNotReadableException) ex);
			}else if(ex instanceof MissingServletRequestParameterException){
				return handleMissingServletRequestParameterException(request, response, handler,
						(MissingServletRequestParameterException) ex);
			}else if(ex instanceof ServletRequestBindingException){
				return handleServletRequestBindingException(request, response, handler,
						(ServletRequestBindingException) ex);
			}else if(ex instanceof NoHandlerFoundException){
				return handleNoHandlerFoundException(request, response, handler, (NoHandlerFoundException) ex);
			}else if(ex instanceof HttpRequestMethodNotSupportedException){
				return handleHttpRequestMethodNotSupportedException(request, response, handler,
						(HttpRequestMethodNotSupportedException) ex);
			}else if(ex instanceof HttpMediaTypeNotAcceptableException){
				return handleHttpMediaTypeNotAcceptableException(request, response, handler,
						(HttpMediaTypeNotAcceptableException) ex);
			}else if(ex instanceof HttpMediaTypeNotSupportedException){
				return handleHttpMediaTypeNotSupportedException(request, response, handler,
						(HttpMediaTypeNotSupportedException) ex);
			}else if(ex instanceof MissingPathVariableException){
				return handleMissingPathVariableException(request, response, handler,
						(MissingPathVariableException) ex);
			}else if(ex instanceof ConversionNotSupportedException){
				return handleConversionNotSupportedException(request, response, handler,
						(ConversionNotSupportedException) ex);
			}else if(ex instanceof HttpMessageNotWritableException){
				return handleHttpMessageNotWritableException(request, response, handler,
						(HttpMessageNotWritableException) ex);
			}else if(ex instanceof AsyncRequestTimeoutException){
				return handleAsyncRequestTimeoutException(request, response, handler,
						(AsyncRequestTimeoutException) ex);
			}
		}catch(Exception handlerEx){
			if(logger.isWarnEnabled()){
				logger.warn("Failure while trying to resolve exception [{}]", ex.getClass().getName(), handlerEx);
			}
		}

		return doDefaultResolveException(request, response, handler, ex);
	}

	protected ModelAndView doSpecialResolveException(final HttpServletRequest request,
													 final HttpServletResponse response, final Object handler,
													 final Exception ex) {
		return null;
	}

	protected ModelAndView doDefaultResolveException(final HttpServletRequest request,
													 final HttpServletResponse response, final Object handler,
													 final Exception ex) {
		return null;
	}

	/**
	 * Status code: 400
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link MethodArgumentNotValidException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleMethodArgumentNotValidException(final HttpServletRequest request,
																 final HttpServletResponse response,
																 @Nullable final Object handler,
																 final MethodArgumentNotValidException ex) {
		response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 400
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link MissingServletRequestPartException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleMissingServletRequestPartException(final HttpServletRequest request,
																	final HttpServletResponse response,
																	@Nullable final Object handler,
																	final MissingServletRequestPartException ex) {
		response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 400
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link BindException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleBindException(final HttpServletRequest request, final HttpServletResponse response,
											   @Nullable final Object handler, final BindException ex) {
		response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 400
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link MissingServletRequestParameterException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleMissingServletRequestParameterException(final HttpServletRequest request,
																		 final HttpServletResponse response,
																		 @Nullable final Object handler,
																		 final MissingServletRequestParameterException ex) {
		response.setStatus(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 400
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link ServletRequestBindingException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleServletRequestBindingException(final HttpServletRequest request,
																final HttpServletResponse response,
																@Nullable final Object handler,
																final ServletRequestBindingException ex) {
		response.setStatus(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 400
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link TypeMismatchException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleTypeMismatchException(final HttpServletRequest request,
													   final HttpServletResponse response,
													   @Nullable final Object handler,
													   final TypeMismatchException ex) {
		response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 400
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link HttpMessageNotReadableException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleHttpMessageNotReadableException(final HttpServletRequest request,
																 final HttpServletResponse response,
																 @Nullable final Object handler,
																 final HttpMessageNotReadableException ex) {
		response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 404
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link NoHandlerFoundException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleNoHandlerFoundException(final HttpServletRequest request,
														 final HttpServletResponse response,
														 @Nullable final Object handler,
														 final NoHandlerFoundException ex) {
		pageNotFoundLogger.warn(ex.getMessage());
		response.setStatus(HttpServletResponse.SC_NOT_FOUND);
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 405
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link HttpRequestMethodNotSupportedException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleHttpRequestMethodNotSupportedException(final HttpServletRequest request,
																		final HttpServletResponse response,
																		@Nullable final Object handler,
																		final HttpRequestMethodNotSupportedException ex) {
		response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());

		String[] supportedMethods = ex.getSupportedMethods();
		if(supportedMethods != null){
			response.setHeader("Allow", Arrays.toString(supportedMethods, ", "));
		}

		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 406
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link HttpMediaTypeNotAcceptableException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleHttpMediaTypeNotAcceptableException(final HttpServletRequest request,
																	 final HttpServletResponse response,
																	 @Nullable final Object handler,
																	 final HttpMediaTypeNotAcceptableException ex) {
		response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 415
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link HttpMediaTypeNotSupportedException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleHttpMediaTypeNotSupportedException(final HttpServletRequest request,
																	final HttpServletResponse response,
																	@Nullable final Object handler,
																	final HttpMediaTypeNotSupportedException ex) {
		response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);

		List mediaTypes = ex.getSupportedMediaTypes();
		if(!CollectionUtils.isEmpty(mediaTypes)){
			response.setHeader("Accept", MediaType.toString(mediaTypes));
		}

		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 500
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link MissingPathVariableException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleMissingPathVariableException(final HttpServletRequest request,
															  final HttpServletResponse response,
															  @Nullable final Object handler,
															  final MissingPathVariableException ex) {
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage());
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 500
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link ConversionNotSupportedException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleConversionNotSupportedException(final HttpServletRequest request,
																 final HttpServletResponse response,
																 @Nullable final Object handler,
																 final ConversionNotSupportedException ex) {
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 500
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link HttpMessageNotWritableException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleHttpMessageNotWritableException(final HttpServletRequest request,
																 final HttpServletResponse response,
																 @Nullable final Object handler,
																 final HttpMessageNotWritableException ex) {
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return doResolve(request, response, handler, ex);
	}

	/**
	 * Status code: 503
	 *
	 * @param request
	 *        {@link HttpServletRequest}
	 * @param response
	 *        {@link HttpServletResponse}
	 * @param handler
	 * 		the executed handler, or {@code null} if none chosen at the time of the exception (for example, if
	 * 		multipart resolution failed)
	 * @param ex
	 *        {@link AsyncRequestTimeoutException}
	 *
	 * @return 返回数据
	 */
	protected ModelAndView handleAsyncRequestTimeoutException(final HttpServletRequest request,
															  final HttpServletResponse response,
															  @Nullable final Object handler,
															  final AsyncRequestTimeoutException ex) {
		if(response.isCommitted() == false){
			response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
		}else{
			logger.warn("Async request timed out");
		}

		return doResolve(request, response, handler, ex);
	}

	protected boolean acceptTextHtml(final HttpServletRequest request) {
		return true;
	}

	protected boolean acceptJson(final HttpServletRequest request) {
		final String contentType = request.getHeader("Content-Type");
		return contentType != null && contentType.contains(MediaType.APPLICATION_JSON_VALUE);
	}

	protected ModelAndView createModelAndView(final HttpServletRequest request, final HttpServletResponse response,
											  final HttpStatus httpStatus, final Exception ex) {
		return acceptJson(request) ? new ModelAndView(new MappingJackson2JsonView()) :
				new ModelAndView(determineViewName(request, response, ex, httpStatus));
	}

	@Deprecated
	protected ModelAndView doResolve(final HttpServletRequest request, final HttpServletResponse response,
									 final Exception ex) {
		return doResolve(request, response, null, ex);
	}

	protected ModelAndView doResolve(final HttpServletRequest request, final HttpServletResponse response,
									 @Nullable final Object handler, final Exception ex) {
		request.setAttribute("javax.servlet.error.exception", ex);

		HttpStatus httpStatus = HttpStatus.resolve(response.getStatus());

		ModelAndView mv = createModelAndView(request, response, httpStatus, ex);

		mv.addObject("state", false);
		mv.addObject("code", response.getStatus());
		mv.addObject("message", httpStatus.getReasonPhrase());
		mv.addObject("status", httpStatus);
		mv.addObject("timestamp", new Date());
		mv.addObject(getExceptionAttribute(), ex);

		applyStatusCodeIfPossible(request, response, httpStatus);

		return mv;
	}

	protected String determineViewName(final HttpServletRequest request, final HttpServletResponse response,
									   final Exception ex, final HttpStatus httpStatus) {
		String viewName = null;

		if(getExceptionViews() != null){
			viewName = getExceptionViews().get(ex);
		}

		if(viewName == null && getErrorViews() != null){
			viewName = getErrorViews().get(httpStatus);
		}

		if(viewName == null){
			viewName = SERIES_VIEWS.get(httpStatus.series());
		}

		if(viewName == null && getDefaultErrorView() != null){
			if(logger.isDebugEnabled()){
				logger.debug("Resolving to default view '{}' for exception of type [{}]", getDefaultErrorView(),
						ex.getClass().getName());
			}

			viewName = getDefaultErrorView();
		}

		return viewName;
	}

	protected void applyStatusCodeIfPossible(final HttpServletRequest request, final HttpServletResponse response,
											 final HttpStatus statusCode) {
		if(WebUtils.isIncludeRequest(request) == false){
			logger.debug("Applying HTTP status code {}", statusCode);

			if(getCacheControl() != null){
				response.setHeader("Cache-Control", getCacheControl());
			}
			request.setAttribute(WebUtils.ERROR_STATUS_CODE_ATTRIBUTE, statusCode);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy