2018-12-19 · Develop

Spring Boot 异常处理

在 Spring Boot 中, Controller 中抛出的异常默认交给了 /error 来处理,具体是的实现类类是 BasicErrorController ,并在 Servlet 容器中注册 error 为全局错误页。所以在错误发生时,可以看见错误信息和 HTTP 状态信息等。

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

	@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) ? modelAndView : new ModelAndView("error", model);
	}

	@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<>(body, status);
	}

默认的异常处理方式肯定是不能满足需求的,对于这种统一的异常我们可以使用两种方式进行处理 AbstractErrorController@ExceptionHandler

由于 @ExceptionHandler 注解需要配合 @RestControllerAdvice 注解进行使用,如果是 404 之类的请求,未找到对应的 Controller 的方法去处理,也就不会走切面的逻辑,这样的话 Spring Boot 就会向 Tomcat 等容器设置 HTTP 状态,容器发现状态未 404 就去找 /error 这个路径,也就走了 BasicErrorController 的处理逻辑。这就导致错误处理的方式不一致。而 AbstractErrorController 这种方式却可以处理全部的异常。简单来说**@ExceptionHandler不能处理404之类的异常,AbstractErrorController的功能更强大,能够处理全部异常**。

通过上面的结果我们知道,针对想 404 之类的异常配置相应的错误页面即可,然后为 Ajax 之类的请求,通过定制自己的 JSON 格式,进行统一返回。

\---src
    \---main
        \---resources
            \---error
                    404.html
                    503.html

然后定制 JSON 的返回。

@RestControllerAdvice
@Slf4j
public class PmsExceptionHandler {
 
    // TODO 处理自定义异常

    @ExceptionHandler(NoHandlerFoundException.class)
    public R<Void> handlerNoFoundException(Exception e) {
        log.error(e.getMessage(), e);
        return R.<Void>builder().error(HttpStatus.SC_NOT_FOUND, "路径不存在,请检查路径是否正确");
    }

    @ExceptionHandler(BindException.class)
    public R<Void> handleBindException(BindException e) {
        log.error(e.getMessage(), e);
        return R.<Void>builder().error("参数错误,请重新输入参数");
    }

    @ExceptionHandler(AuthorizationException.class)
    public R<Void> handleAuthorizationException(AuthorizationException e) {
        log.error(e.getMessage(), e);
        return R.<Void>builder().error("没有权限,请联系管理员授权");
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public R<Void> handleIllegalArgumentException(IllegalArgumentException e) {
        log.error(e.getMessage(), e);
        return R.<Void>builder().error(e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public R<Void> handleException(Exception e) {
        log.error(e.getMessage(), e);
        return R.<Void>builder().error();
    }
}