SpringBoot的异常处理,主要是指对异常进行包装,以友好的方式返回给调用方。处理方式主要通过@RestControllerAdvice、@ExceptionHandler来处理。
- 定义一个统一返回对象
@Data
@Accessors(chain = true)
public class BaseResponse implements Serializable {
private String code;
private String message;
}
- 异常拦截
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 非预期的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.OK)
public BaseResponse tryRuntimeException(HttpServletRequest request, HandlerMethod handlerMethod, RuntimeException e) {
log.error("系统异常,url={},{}#{}", request.getRequestURL(), handlerMethod.getClass(), handlerMethod.getMethod().getName(), e);
return new BaseResponse().setCode("10").setMessage("出错了");
}
/**
* 业务异常
*/
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.OK)
public BaseResponse tryBusinessException(HttpServletRequest request, HandlerMethod handlerMethod, BusinessException e) {
log.error("业务异常,url={},{}#{}", request.getRequestURL(), handlerMethod.getClass(), handlerMethod.getMethod().getName(), e);
return new BaseResponse().setCode("20").setMessage("系统繁忙");
}
/**
* 参数校验异常
*/
@ExceptionHandler({
MethodArgumentNotValidException.class,
BindException.class,
ValidationException.class,
ConstraintViolationException.class})
@ResponseStatus(HttpStatus.OK)
public BaseResponse tryValidationException(HttpServletRequest request, HandlerMethod handlerMethod, Exception e) {
log.error("参数校验异常,url={},{}#{}", request.getRequestURL(), handlerMethod.getClass(), handlerMethod.getMethod().getName(), e);
return new BaseResponse().setCode("30").setMessage("参数不合法");
}
}
注意:这个类确保能被纳入Spring的管理
通过@ExceptionHandler我们可以拦截想要处理的异常类型,一般我们可以把异常分为三类:一类为系统错误,二类为业务异常,三类为参数校验异常。方法名可以随意定义,request,response,handlerMethod,Exception这些参数都不是必须的,也都可以由SpringBoot传入进来。由于SpringBoot通常返回的都是JSON格式数据(@RestControllerAdvice拦截就是RestController,这里返回的对象也会就会被转成JSON)。只有一些特殊的情况,类似SpringMVC的方式,可以通过request的请求头和Controller的配置来判断是返回JSON还是错误页面。
/**
* 根据HttpServletRequest请求头来判断是否是ajax请求
*/
public static boolean isAsyncRequest(HttpServletRequest request) {
String requestHeader = request.getHeader("x-requested-with");
if (StringUtils.isEmpty(requestHeader) && "XMLHttpRequest".equals(requestHeader)) {
return true;
} else {
return false;
}
}
/**
* 根据handlerMethod的注解来判断是不是ajax请求
*/
public static boolean isAsyncRequest(HttpServletRequest request, HandlerMethod handlerMethod) {
boolean isAjax = isAsyncRequest(request);
if (isAjax) {
return true;
}
// 方法上有@ResponseBody 表示返回JSON
ResponseBody responseBody = handlerMethod.getMethodAnnotation(ResponseBody.class);
if (null != responseBody) {
return true;
}
//判断Controller类上有没有@RestController
RestController restController = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), RestController.class);
if (null != restController) {
return true;
}
//判断Controller类上有没有@ResponseBody
responseBody = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), ResponseBody.class);
if (null != responseBody) {
return true;
}
return false;
}
对异常处理后,根据请求类型,返回ModelAndView
/**
* 对异常处理后,根据请求类型,返回ModelAndView
*/
@ExceptionHandler(Exception.class)
public ModelAndView businessException(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception ex) {
if (isAsyncRequest(request, handlerMethod)) {
return buildJsonResponse(request, response, handlerMethod, ex);
} else {
return buildViewResponse(request, response, handlerMethod, ex);
}
}
private ModelAndView buildJsonResponse(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception ex) {
BaseResponse baseResponse = new BaseResponse().setCode("40").setMessage(ex.getMessage());
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
writer.write(JSON.toJSONString(baseResponse));
return null;
}
private ModelAndView buildViewResponse(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception ex) {
ModelAndView errorView = new ModelAndView();
errorView.setViewName("redirect:/error-page");
return errorView;
}
网友评论