【五】Spring Boot之 全局异常

版权声明:转载注明出处 https://blog.csdn.net/jy02268879/article/details/83151272

一、一次请求分成三个阶段,来分别进行全局的异常处理

第一阶段:在进入Controller之前,譬如请求一个不存在的地址,404错误。 
第二阶段:在执行@RequestMapping时,进入逻辑处理阶段前。譬如传的参数类型错误。 
第三阶段:以上都正常时,在controller里执行逻辑代码时出的异常。譬如NullPointerException
 

二、SpringBoot内置异常 处理BasicErrorController

BasicErrorController提供两种错误返回

@RequestMapping(produces = "text/html")
    public ModelAndView errorHtml(HttpServletRequest request,
            HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
                request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
    }

    @RequestMapping
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }

1.页面请求发生错误则把请求转到/error上。

页面发生异常时会把请求转到/error。这个/error路径可以在application.yml上自定义

server:
  port: 8080
  error:
    path: /custom/error

2.json请求发生错误则会返回json错误。

{
  "timestamp": 1478571808052,
  "status": 404,
  "error": "Not Found",
  "message": "No message available",
  "path": "/all"
}

 

三、自定义异常的两种方案

1.使用@ControllerAdvice注解

Controller层的全局异常统一处理。这能处理上述第二、三阶段的发生的异常。

无法根据不同的头部返回不同的数据格式,而且无法针对404、403等多种状态进行处理。

@ControllerAdvice 主要是用来Controller的一些公共的需求的低侵入性增强提供辅助,作用于@RequestMapping标注的方法上。

换句话说就是,这里能够捕获到的异常,是由前端调用controller层@RequestMapping标注的方法内产生的。


@ExceptionHandler(value = Exception.class)

该注解是配合@ExceptionHandler一起使用的注解,自定义错误处理器,可自己组装json字符串,并返回到页面。

value中指定该方法需要处理的那些异常类。

如果返回的是json,不是view,需要在方法上添加@ResponseBody

下图代码,是对Exception和自定义的BizException进行不同的处理,写得比较粗糙。正常情况下,出于安全考虑,Exception信息只打印日志,不暴露给前端。

import common.request.spring.boot.autoconfigure.exception.BizException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;



/**
 * @program: 
 * @description: 全局异常 ControllerAdvice方式
 * @author: liyijie
 * @create: 2018-10-18 17:07
 **/
@Order(value = -1)
@ControllerAdvice //主要是用来Controller的一些公共的需求的低侵入性增强提供辅助,作用于@RequestMapping标注的方法上。
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 全局异常捕捉处理
     * @param e
     * @return
     */
    //该注解是配合@ExceptionHandler一起使用的注解,自定义错误处理器,可自己组装json字符串,并返回到页面。
    //value中指定该方法需要处理的那些异常类
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public MyResponse<Void> exceptionHandler(Exception e) {
        logger.error(e.getMessage(),e);
        return new MyResponse<>(MyResponse.CODE_FAIL,null,e.toString(),System.currentTimeMillis());
    }

    /**
     * 拦截捕捉自定义异常 BizException.class
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = BizException.class)
    public MyResponse<Void> BizExceptionHandler(BizException e) {
        return new MyResponse<>(MyResponse.CODE_FAIL,null,e.getMsg(),System.currentTimeMillis());
    }
}

如果不起作用,请检查两个点:

1.看一下spring启动类,看一下这个全局异常处理类,spring有没有把它扫描进来!

2.看旗下spring启动日志,是不是引的别的同事的包或者用了什么鬼东西,在加载自己的全局异常处理类之前就已经加载了别人的全局异常处理类了。解决方法:添加@order(value=-1),这样就是先加载自己写的这个全局异常处理类。

比如我就是这个问题。在加载我的之前就加载了另一个全局异常处理类exAdvice

2018-10-18 19:08:53.344 INFO [main]o.s.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.initExceptionHandlerAdviceCache:273 -Detected @ExceptionHandler methods in exAdvice
2018-10-18 19:08:53.345 INFO [main]o.s.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.initExceptionHandlerAdviceCache:273 -Detected @ExceptionHandler methods in globalExceptionHandler

2.实现ErrorController接口或者继承AbstractErrorController抽象类或者继承BasicErrorController类

演示实现ErrorController。这能处理上述第一阶段发生的异常。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes;

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

/**
 * @description: 处理全局异常 请求进入@RequestMapping方法之前的异常
 * @create: 2018-10-18 20:34
 **/
@Controller
@EnableConfigurationProperties({ServerProperties.class})
public class ExceptionController implements ErrorController {

    private ErrorAttributes errorAttributes;
 
    @Autowired
    private ServerProperties serverProperties;
 
 
    /**
     * 初始化ExceptionController
     * @param errorAttributes
     */
    @Autowired
    public ExceptionController(ErrorAttributes errorAttributes) {
        Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
        this.errorAttributes = errorAttributes;
    }
 
 
    /**
     * 定义404的ModelAndView
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(produces = "text/html",value = "404")
    public ModelAndView errorHtml404(HttpServletRequest request,
                                  HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.TEXT_HTML));
        return new ModelAndView("error/404", model);
    }
 
    /**
     * 定义404的JSON数据
     * @param request
     * @return
     */
    @RequestMapping(value = "404")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error404(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.TEXT_HTML));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }
 
    /**
     * 定义500的ModelAndView
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(produces = "text/html",value = "500")
    public ModelAndView errorHtml500(HttpServletRequest request,
                                  HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.TEXT_HTML));
        return new ModelAndView("error/500", model);
    }
 
 
    /**
     * 定义500的错误JSON信息
     * @param request
     * @return
     */
    @RequestMapping(value = "500")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error500(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.TEXT_HTML));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }
 
 
    /**
     * Determine if the stacktrace attribute should be included.
     * @param request the source request
     * @param produces the media type produced (or {@code MediaType.ALL})
     * @return if the stacktrace attribute should be included
     */
    protected boolean isIncludeStackTrace(HttpServletRequest request,
                                          MediaType produces) {
        ErrorProperties.IncludeStacktrace include = this.serverProperties.getError().getIncludeStacktrace();
        if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
            return true;
        }
        if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
            return getTraceParameter(request);
        }
        return false;
    }
 
 
    /**
     * 获取错误的信息
     * @param request
     * @param includeStackTrace
     * @return
     */
    private Map<String, Object> getErrorAttributes(HttpServletRequest request,
                                                   boolean includeStackTrace) {
        RequestAttributes requestAttributes = new ServletRequestAttributes(request);
        return this.errorAttributes.getErrorAttributes(requestAttributes,
                includeStackTrace);
    }
 
    /**
     * 是否包含trace
     * @param request
     * @return
     */
    private boolean getTraceParameter(HttpServletRequest request) {
        String parameter = request.getParameter("trace");
        if (parameter == null) {
            return false;
        }
        return !"false".equals(parameter.toLowerCase());
    }
 
    /**
     * 获取错误编码
     * @param request
     * @return
     */
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request
                .getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        try {
            return HttpStatus.valueOf(statusCode);
        }
        catch (Exception ex) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }
 
    /**
     * 实现错误路径,暂时无用
     * @see ExceptionMvcAutoConfiguration#containerCustomizer()
     * @return
     */
    @Override
    public String getErrorPath() {
        return "";
    }


}

或者另一种自定实现方式:继承AbstractErrorController抽象类。

@Controller
    @RequestMapping("${server.error.path:${error.path:/error}}")
    public class BasicErrorController extends AbstractErrorController {
        private final ErrorProperties errorProperties;
        private static final Logger LOGGER = LoggerFactory.getLogger(BasicErrorController.class);
        @Autowired
        private ApplicationContext applicationContext;

        /**
         * Create a new {@link org.springframework.boot.autoconfigure.web.BasicErrorController} instance.
         *
         * @param errorAttributes the error attributes
         * @param errorProperties configuration properties
         */
        public BasicErrorController(ErrorAttributes errorAttributes,
                                    ErrorProperties errorProperties) {
            this(errorAttributes, errorProperties,
                    Collections.<ErrorViewResolver>emptyList());
        }

        /**
         * Create a new {@link org.springframework.boot.autoconfigure.web.BasicErrorController} instance.
         *
         * @param errorAttributes    the error attributes
         * @param errorProperties    configuration properties
         * @param errorViewResolvers error view resolvers
         */
        public BasicErrorController(ErrorAttributes errorAttributes,
                                    ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
            super(errorAttributes, errorViewResolvers);
            Assert.notNull(errorProperties, "ErrorProperties must not be null");
            this.errorProperties = errorProperties;
        }

        @Override
        public String getErrorPath() {
            return this.errorProperties.getPath();
        }

        @RequestMapping(produces = "text/html")
        public ModelAndView errorHtml(HttpServletRequest request,
                                      HttpServletResponse response) {
            HttpStatus status = getStatus(request);
            Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
                    request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
            response.setStatus(status.value());
            ModelAndView modelAndView = resolveErrorView(request, response, status, model);
            insertError(request);
            return modelAndView == null ? new ModelAndView("error", model) : modelAndView;
        }



        @RequestMapping
        @ResponseBody
        public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
            Map<String, Object> body = getErrorAttributes(request,
                    isIncludeStackTrace(request, MediaType.ALL));
            HttpStatus status = getStatus(request);
            insertError(request);
            return new ResponseEntity(body, status);
        }

        /**
         * Determine if the stacktrace attribute should be included.
         *
         * @param request  the source request
         * @param produces the media type produced (or {@code MediaType.ALL})
         * @return if the stacktrace attribute should be included
         */
        protected boolean isIncludeStackTrace(HttpServletRequest request,
                                              MediaType produces) {
            ErrorProperties.IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
            if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
                return true;
            }
            if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
                return getTraceParameter(request);
            }
            return false;
        }

        /**
         * Provide access to the error properties.
         *
         * @return the error properties
         */
        protected ErrorProperties getErrorProperties() {
            return this.errorProperties;
        }
    }

 

展开阅读全文

没有更多推荐了,返回首页