欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringMVC 统一异常处理

程序员文章站 2022-05-28 16:17:41
...
先说说 网上流传的几种处理方式:
方式1:使用SimpleMappingExceptionResolver实现异常处理  (试验过, 对于表单请求异常 和ajax 请求异常处理不方便) 实验的结果就是判断 如果出现404 500错误,判断不出来源是ajax 还是 普通页面请求  所以放弃了
package cn.mwee.wpos.report.cache;

import cn.mwee.utils.error.ThrowableUtil;
import cn.mwee.wpos.report.consts.ResultCode;
import cn.mwee.wpos.service.conts.ErrorCode;
import cn.mwee.wpos.service.conts.ErrorDesc;
import cn.mwee.wpos.service.dto.common.ErrorResult;
import com.alibaba.fastjson.JSON;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;


/**
 * Created by luob on 2017/6/27.
 */
public class GlobalExceptionResolver extends SimpleMappingExceptionResolver {
   @Override
   protected ModelAndView doResolveException(HttpServletRequest request,
                          HttpServletResponse response, Object handler, Exception ex){
             String viewName = determineViewName(ex,request);
             response.setCharacterEncoding("UTF-8");

             String requestType = request.getHeader("X-Requested-With");
             if (viewName != null) {// JSP格式返回
                 if (!(request.getHeader("accept").indexOf("application/json") > -1 || (request
                         .getHeader("X-Requested-With") != null && request
                         .getHeader("X-Requested-With").indexOf("XMLHttpRequest") > -1))) {
                        // 如果不是异步请求
                             // Apply HTTP status code for error views, if specified.
                             // Only apply it if we're processing a top-level request.
                             Integer statusCode = determineStatusCode(request, viewName);
                             if (statusCode != null){
                                 applyStatusCodeIfPossible(request, response, statusCode);
                             }
                             return getModelAndView(viewName, ex, request);
                         } else {// JSON格式返回
                             try {
                                     PrintWriter writer = response.getWriter();
                                     Map<String,Object>  result=new HashMap<>();
                                     ErrorResult error=new ErrorResult(ErrorCode.FAIL, ErrorDesc.FAIL, ThrowableUtil.getStackTrace(ex));
                                     result.put(ResultCode.ERROR,error);
                                     writer.write(JSON.toJSONString(result));
                                     writer.flush();
                                     writer.close();
                                 } catch (IOException e) {
                                     e.printStackTrace();
                                 }
                             return null;
                         }
             } else{
                 return null;
             }
         }
    
}


方法二:@ExceptionHandler + @ControllerAdvice
问题是:可以正确获取 所有的异常 ,但是request 和response 获取不到 异常后 应该返回的 status 比如 404  500 415
package cn.mwee.wpos.report.controller;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
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.ControllerAdvice;
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.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
@Slf4j
public class ErrorHandlerController {

    @ExceptionHandler(value = Exception.class)
    public String exception(HttpServletRequest request, Exception ex) {
        log.info("ErrorHandlerController.exception");
        request.setAttribute("httpStatus", Integer.toString(getHttpStatus(ex).value()));
        request.setAttribute("httpMessage", getHttpStatus(ex).getReasonPhrase());
        request.setAttribute("errorType", ex.getClass().getName());
        request.setAttribute("errorMessage", ex.getMessage());
        request.setAttribute("errorStackTrace", ExceptionUtils.getStackTrace(ex));
       //必须是 forward 否则 /error 区分不了 ajax 和普通的请求
        return "forward:/error";
    }

    public HttpStatus getHttpStatus(Exception ex) {
        if (ex instanceof org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException) {
            return HttpStatus.NOT_FOUND;
        } else if (ex instanceof HttpRequestMethodNotSupportedException) {
            return HttpStatus.METHOD_NOT_ALLOWED;
        } else if (ex instanceof HttpMediaTypeNotSupportedException) {
            return HttpStatus.UNSUPPORTED_MEDIA_TYPE;
        } else if (ex instanceof HttpMediaTypeNotAcceptableException) {
            return HttpStatus.NOT_ACCEPTABLE;
        } else if (ex instanceof MissingPathVariableException) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        } else if (ex instanceof MissingServletRequestParameterException) {
            return HttpStatus.BAD_REQUEST;
        } else if (ex instanceof ServletRequestBindingException) {
            return HttpStatus.BAD_REQUEST;
        } else if (ex instanceof ConversionNotSupportedException) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        } else if (ex instanceof TypeMismatchException) {
            return HttpStatus.BAD_REQUEST;
        } else if (ex instanceof HttpMessageNotReadableException) {
            return HttpStatus.BAD_REQUEST;
        } else if (ex instanceof HttpMessageNotWritableException) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        } else if (ex instanceof MethodArgumentNotValidException) {
            return HttpStatus.BAD_REQUEST;
        } else if (ex instanceof MissingServletRequestPartException) {
            return HttpStatus.BAD_REQUEST;
        } else if (ex instanceof BindException) {
            return HttpStatus.BAD_REQUEST;
        } else if (ex instanceof NoHandlerFoundException) {
            return HttpStatus.NOT_FOUND;
        } else if (ex instanceof AsyncRequestTimeoutException) {
            return HttpStatus.SERVICE_UNAVAILABLE;
        } else {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }
}


方法3: web.xml + controller 来实现
web.xml 中配置
    <!--error page-->
    <error-page>
        <location>/error</location>
    </error-page>

package cn.mwee.wpos.report.controller.error;

import cn.mwee.utils.error.ThrowableUtil;
import cn.mwee.utils.log4j2.LogData;
import cn.mwee.utils.uuid.UUIDUtil;
import cn.mwee.wpos.common.utils.AlfredCommonUtils;
import cn.mwee.wpos.report.consts.ResultCode;
import cn.mwee.wpos.service.conts.ErrorCode;
import cn.mwee.wpos.service.conts.ErrorDesc;
import cn.mwee.wpos.service.dto.common.ErrorResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.util.WebUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Controller
public class ErrorController {
    /**
     * 异常页面
     * @param ex
     * @param response
     * @return
     */
    @RequestMapping(path = "/error")
    public ModelAndView errorHtml(HttpServletRequest request,Exception ex,HttpServletResponse response) {
        int status=response.getStatus();
        ModelAndView modelAndView;
        if(status == 403 || status == 404 || status == 500) {
            modelAndView = new ModelAndView("error/" +status);
        }else{
            modelAndView = new ModelAndView("error/error");
        }
        modelAndView.addObject("status",status);
        return modelAndView;
    }

    /**
     * 异常json
     * @param ex
     * @param response
     * @return
     */
    @RequestMapping(path = "/error",consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public Map<String, Object> errorJson(HttpServletRequest request,Exception ex,HttpServletResponse response) {
        Map<String,Object> result=new HashMap<>();
        Object obj=request.getAttribute(DispatcherServlet.EXCEPTION_ATTRIBUTE);
        if(obj == null){
            obj=request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE);
        }
        if(obj !=null && obj instanceof Exception){
            ex=(Exception)obj;
        }
        ErrorResult error=new ErrorResult(ErrorCode.FAIL, ErrorDesc.FAIL, ThrowableUtil.getStackTrace(ex));
        result.put(ResultCode.ERROR,error);
        return result;
    }

}