SpringBoot全局异常处理

1、异常处理的种类

我大概把一次请求分成三个阶段,来分别进行全局的异常处理。

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

2、第一种情况

2.1、报错情况

当使用SpringBoot调用controller时,如果系统出现404,405,500等这种报错信息时,系统默认会展示如下信息:

SpringBoot全局异常处理_第1张图片

2.2、SpringBoot默认异常处理BasicErrorController

SpringBoot的默认异常处理BasicErrorController,它会根据配置参数server.error.path来展示错误页面。BasicErrorController这两个方法是关键。

  • getErrorPath()错误页路径
  • errorHtml()返回错误页信息
 public String getErrorPath() {
        return this.errorProperties.getPath();
    }

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

2.3、自定义错误异常

通过研究BasicErrorController,我们自定义错误异常需要实现ErrorContrroller大概需要四点。

  • 实现ErrorController接口,并重写getErrorPath()方法,指定异常跳转的url;
  • 对类添加@RestController注解,添加异常跳转url的Controller;
  • 获取请求响应的类型,针对不同的响应错误类型,做不同的逻辑处理。
  • 在template目录下存放错误页面的HTML文件。
@Slf4j
@Controller
@RequestMapping("/error")
@EnableConfigurationProperties({ServerProperties.class})
public class ErrorPagesController implements ErrorController {

    private ErrorAttributes errorAttributes;

    @Autowired
    private ServerProperties serverProperties;

    /**
     * 初始化ExceptionController
     *
     * @param errorAttributes
     */
    @Autowired
    public ErrorPagesController(ErrorAttributes errorAttributes) {
        Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
        this.errorAttributes = errorAttributes;
    }

    @RequestMapping("/404")
    public ModelAndView errorHtml404(HttpServletRequest request, HttpServletResponse response, WebRequest webRequest) {
        response.setStatus(HttpStatus.NOT_FOUND.value());
        Map<String, Object> model = getErrorAttributes(webRequest, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        model.put("queryString", request.getQueryString());

        return new ModelAndView("error/404", model);
    }

    @RequestMapping("/403")
    public ModelAndView errorHtml403(HttpServletRequest request, HttpServletResponse response, WebRequest webRequest) {
        response.setStatus(HttpStatus.FORBIDDEN.value());
        // 404拦截规则,如果是静态文件发生的404则不记录到DB
        Map<String, Object> model = getErrorAttributes(webRequest, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        model.put("queryString", request.getQueryString());
        if (!String.valueOf(model.get("path")).contains(".")) {
            model.put("status", HttpStatus.FORBIDDEN.value());
        }
        return new ModelAndView("error/403", model);
    }

    @RequestMapping("/400")
    public ModelAndView errorHtml400(HttpServletRequest request, HttpServletResponse response, WebRequest webRequest) {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        Map<String, Object> model = getErrorAttributes(webRequest, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        model.put("queryString", request.getQueryString());
        return new ModelAndView("error/400", model);
    }

    @RequestMapping("/401")
    public ModelAndView errorHtml401(HttpServletRequest request, HttpServletResponse response, WebRequest webRequest) {
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        Map<String, Object> model = getErrorAttributes(webRequest, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        model.put("queryString", request.getQueryString());
        return new ModelAndView("error/401", model);
    }

    @RequestMapping("/500")
    public ModelAndView errorHtml500(HttpServletRequest request, HttpServletResponse response, WebRequest webRequest) {
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        Map<String, Object> model = getErrorAttributes(webRequest, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        model.put("queryString", request.getQueryString());
        return new ModelAndView("error/500", model);
    }

    /**
     * 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;
        }
        return include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM && getTraceParameter(request);
    }


    /**
     * 获取错误的信息
     *
     * @param webRequest
     * @param includeStackTrace
     * @return
     */
    private Map<String, Object> getErrorAttributes(WebRequest webRequest,
                                                   boolean includeStackTrace) {
        return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
    }

    /**
     * 是否包含trace
     *
     * @param request
     * @return
     */
    private boolean getTraceParameter(HttpServletRequest request) {
        String parameter = request.getParameter("trace");
        return parameter != null && !"false".equalsIgnoreCase(parameter);
    }

    /**
     * 获取错误编码
     *
     * @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) {
            log.error("获取当前HttpStatus发生异常", ex);
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }

    /**
     * 实现错误路径,暂时无用
     *
     * @return
     */
    @Override
    public String getErrorPath() {
        return "";
    }
}

3、第二种情况

在执行@RequestMapping时,进入逻辑处理阶段前。譬如传的参数类型错误。

/**
 * Created by wuwf on 17/3/31.
 * 全局异常处理
 */
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
    private static Logger logger = LogManager.getLogger(GlobalExceptionHandler.class.getName());

    /**
     * 在controller里面内容执行之前,校验一些参数不匹配啊,Get post方法不对啊之类的
     */
    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        System.out.println("错误");
		// TODO
        return new ResponseEntity<Object>("出错了", NOT_EXTENDED);

    }
}

定义一个类,使用@ControllerAdvice注解,继承ResponseEntityExceptionHandler类,这个类里面实现了很多方法,可以去看看,包括一些参数转换,请求方法不支持等等之类的异常都会被捕获。
被捕获的原因是@ExceptionHandler标签,里面所有的异常类只要发生了,就会被这个方法所捕获。

4、第三种情况

当第一、第二种都没出异常,进入到实际逻辑执行了,然后发生了异常,这样就可以自己定义一个ExceptionHandler的方法,来处理相应的Exception。

/**
 * Created by wuwf on 17/3/31.
 * 全局异常处理
 */
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
    private static Logger logger = LogManager.getLogger(GlobalExceptionHandler.class.getName());

    /**
     * 在controller里面内容执行之前,校验一些参数不匹配啊,Get post方法不对啊之类的
     */
    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        System.out.println("错误");

        return new ResponseEntity<Object>("出错了", NOT_EXTENDED);

    }

	@ExceptionHandler(value = Exception.class)
	@ResponseBody
	public Map<String, String> jsonExceptionHandler(HttpServletRequest req, ServiceException e) {
		Map<String, String> re = new HashMap<String, String>();
		re.put("status", e.getStatus());
		re.put("msg", e.getMessage());
		return re;
	}
}

你可能感兴趣的:(SpringBoot,spring,boot,java,spring)