SpringMVC異常統一處理並返回數據或視圖View

實現方式和SpringMVC攔截器HandlerInterceptor攔截後返回數據或視圖View
一致,這裏不贅述,貼一份代碼參考:


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * @author obiteaaron
 * @since 2019/12/27
 */
@Component
public class SpringMvcExceptionHandlerResolver extends AbstractHandlerExceptionResolver implements ApplicationContextAware {

    private static final Logger LOGGER = LoggerFactory.getLogger(SpringMvcExceptionHandlerResolver.class);

    private ApplicationContext applicationContext;

    @Autowired
    SpringMvcExceptionHandlerResolver.ExceptionHandler exceptionHandler = new SpringMvcExceptionHandlerResolver.ExceptionHandler() {
        private String EXCEPTION_VIEW = "error";

        @Override
        public String getViewName(Exception ex) {
            // 自己實現
            return EXCEPTION_VIEW;
        }

        @Override
        public Object getResponseBody(Exception ex) {
            // 自己實現
            if (ex instanceof IllegalArgumentException) {
                throw new UnsupportedOperationException();
            } else if (ex instanceof UnsupportedOperationException) {
                throw new UnsupportedOperationException();
            } else {
                throw new UnsupportedOperationException();
            }
        }
    };

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        try {
            // 如果被攔截,返回信息
            if (((HandlerMethod) handler).getMethodAnnotation(ResponseBody.class) != null) {
                // 返回json
                HandlerMethod handlerMethod = new HandlerMethod(((HandlerMethod) handler).getBean(), ((HandlerMethod) handler).getMethod());
                Object returnValue = exceptionHandler.getResponseBody(ex);
                MethodParameter returnValueType = handlerMethod.getReturnValueType(returnValue);
                applicationContext.getBean(RequestMappingHandlerAdapter.class).getReturnValueHandlers();
                RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = findRequestResponseBodyMethodProcessor();
                requestResponseBodyMethodProcessor.handleReturnValue(returnValue, returnValueType, new ModelAndViewContainer(), new ServletWebRequest(request, response));
                return new ModelAndView();
                // end
            } else {
                // 返回頁面
                HandlerMethod handlerMethod = new HandlerMethod(((HandlerMethod) handler).getBean(), ((HandlerMethod) handler).getMethod());
                String viewName = exceptionHandler.getViewName(ex);
                MethodParameter returnValueType = handlerMethod.getReturnValueType(viewName);
                ViewNameMethodReturnValueHandler viewNameMethodReturnValueHandler = findViewNameMethodReturnValueHandler();
                ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();
                // viewNameMethodReturnValueHandler 內的實現非常簡單,其實可以不用這個的,直接new ModelAndViewContainer()就好了。
                viewNameMethodReturnValueHandler.handleReturnValue(viewName, returnValueType, modelAndViewContainer, new ServletWebRequest(request, response));

                // 返回ModelAndView由Spring處理
                ModelMap model = modelAndViewContainer.getModel();
                ModelAndView modelAndView = new ModelAndView(modelAndViewContainer.getViewName(), model, modelAndViewContainer.getStatus());
                exceptionHandler.fillModelAndView(ex, modelAndView);
                return modelAndView;
                // end
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private RequestResponseBodyMethodProcessor findRequestResponseBodyMethodProcessor() {
        RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
        for (HandlerMethodReturnValueHandler value : Objects.requireNonNull(requestMappingHandlerAdapter.getReturnValueHandlers())) {
            if (value instanceof RequestResponseBodyMethodProcessor) {
                return (RequestResponseBodyMethodProcessor) value;
            }
        }
        // SpringMVC的環境下一定不會走到這裏
        throw new UnsupportedOperationException("cannot find RequestResponseBodyMethodProcessor from RequestMappingHandlerAdapter by Spring Context.");
    }

    private ViewNameMethodReturnValueHandler findViewNameMethodReturnValueHandler() {
        RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
        for (HandlerMethodReturnValueHandler value : Objects.requireNonNull(requestMappingHandlerAdapter.getReturnValueHandlers())) {
            if (value instanceof ViewNameMethodReturnValueHandler) {
                return (ViewNameMethodReturnValueHandler) value;
            }
        }
        // SpringMVC的環境下一定不會走到這裏
        throw new UnsupportedOperationException("cannot find ViewNameMethodReturnValueHandler from RequestMappingHandlerAdapter by Spring Context.");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public interface ExceptionHandler {
        /**
         * 攔截後返回的視圖名稱
         *
         * @param ex
         * @see ModelAndView
         * @see ViewNameMethodReturnValueHandler
         */
        String getViewName(Exception ex);

        /**
         * 創建視圖後再次填充視圖內容
         *
         * @param ex
         * @see ModelAndView
         * @see ViewNameMethodReturnValueHandler
         */
        default void fillModelAndView(Exception ex, ModelAndView modelAndView) {
        }

        /**
         * 攔截後返回的對象
         *
         * @param ex
         * @see ResponseBody
         * @see RequestResponseBodyMethodProcessor
         */
        Object getResponseBody(Exception ex);
    }
}
發佈了91 篇原創文章 · 獲贊 73 · 訪問量 68萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章