Version: Next

全局控制层拦截

这是一种可以让Controller代码变得简洁的处理方式

在开发过程中,我们在Controller中调用Service,Service中有具体的业务逻辑,如果业务逻辑执行出现了异常,就会将异常抛出到Controller层中,我们就需要在Controller中进行异常处理try catch,一旦写了try catch,代码就会变得繁杂,此外,通常情况下,对于发生异常与没发生异常,我们需要进行不同的响应

  • 希望简化Controller的代码
  • 这种问题出现在响应逻辑
  • 想办法将这种响应阶段的繁杂代码,转移出去
info

使用全局响应拦截,对所有Controller采取一致的响应阶段处理策略,可以把原本try catch实现的功能转嫁到这里来写

全局响应处理

可以统一响应类型为AjaxResponse,即使Controller响应的是HashMap,List等类型,也会被处理为AjaxResponse

  • 实现ResponseBodyAdvice接口,重写supports方法和beforeBodyWrite方法
  • supports方法返回的值为true,程序会进入beforeBodyWrite方法
  • beforeBodyWrite方法中,将MediaType为application/json的响应,全部转换为自定义的AjaxResponse
GlobalResponseAdvice.java
@ControllerAdvice
public class GlobalResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class selectedConvertType,
ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
if (selectedContentType.equalsTypeAndSubtype(MediaType.APPLICATION_JSON)) {
if (body instanceof AjaxResponse) { // 如果本来响应的就是AjaxResponse,放行
AjaxResponse ajaxResponse = (AjaxResponse) body;
serverHttpResponse.setStatusCode(HttpStatus.valueOf(ajaxResponse.getCode()));
return body;
} else {
serverHttpResponse.setStatusCode(HttpStatus.OK);
return AjaxResponse.success(body); // 不是AjaxResponse也把你捣鼓成AjaxResponse
}
}
return body;
}
}

全局请求处理

场景:

前端页面涉及提交时间日期的逻辑,会给后端提交时间日期,但是这个时间日期没有指定格式,也不允许前端提交空时间,这显然不好

  • 写一个全局请求处理,对于任何到达Controller的前端日期数据,进行统一的时间格式化处理,并且允许日期为空
GlobalRequestAdvice.java
@ControllerAdvice
public class GlobalRequestAdvice {
@InitBinder
protected void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
binder.registerCustomEditor(Date.class,
//true表示转换为日期的字符串可以为空,不设置这个值接收空串会报错
new CustomDateEditor(dateFormat, true)
);
}
}

全局异常拦截

所有Controller中的try catch语句全部省略,转移到这里来进行统一的处理

  • 作用在所有Controller上
  • 可以指定捕获特定类型的异常,最后可以定义一个捕获Exception的方法来泛化处理
  • 针对每种类型的异常,使用AjaxResponse,配合自定义的异常类型CustomException,进行个性化的异常信息响应
GlobalExceptionHandler.java
@ControllerAdvice
public class GlobalExceptionHandler {
/* @ExceptionHandler(ModelViewException.class)
public ModelAndView viewExceptionHandler(HttpServletRequest req, ModelViewException e) {
ModelAndView modelAndView = new ModelAndView();
//将异常信息设置如modelAndView
modelAndView.addObject("exception", e);
modelAndView.addObject("url", req.getRequestURL());
modelAndView.setViewName("error");
//返回ModelAndView
return modelAndView;
}*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public AjaxResponse handleBindException(MethodArgumentNotValidException ex) {
FieldError fieldError = ex.getBindingResult().getFieldError();
return AjaxResponse.error(new CustomException(CustomExceptionType.USER_INPUT_ERROR,
fieldError.getDefaultMessage()));
}
@ExceptionHandler(BindException.class)
@ResponseBody
public AjaxResponse handleBindException(BindException ex) {
FieldError fieldError = ex.getBindingResult().getFieldError();
return AjaxResponse.error(new CustomException(CustomExceptionType.USER_INPUT_ERROR,
fieldError.getDefaultMessage()));
}
@ExceptionHandler(IllegalArgumentException.class)
@ResponseBody
public AjaxResponse handleIllegalArgumentException(IllegalArgumentException e) {
return AjaxResponse.error(
new CustomException(CustomExceptionType.USER_INPUT_ERROR,
e.getMessage())
);
}
@ExceptionHandler(DuplicateKeyException.class)
@ResponseBody
public AjaxResponse handleDuplicateKeyException(DuplicateKeyException e) {
return AjaxResponse.error(
new CustomException(CustomExceptionType.USER_INPUT_ERROR,
"您输入的数据与现有数据重复,请检查之后重新输入")
);
}
@ExceptionHandler(CustomException.class)
@ResponseBody
public AjaxResponse customerException(CustomException e) {
if (e.getCode() == CustomExceptionType.SYSTEM_ERROR.getCode()) {
//400异常不需要持久化,将异常信息以友好的方式告知用户就可以
//TODO 将500异常信息持久化处理,方便运维人员处理
e.printStackTrace();
}
return AjaxResponse.error(e);
}
//处理程序员在程序中未能捕获(遗漏的)异常
@ExceptionHandler(Exception.class)
@ResponseBody
public AjaxResponse exception(Exception e) {
//TODO 将异常信息持久化处理,方便运维人员处理
e.printStackTrace();
return AjaxResponse.error(new CustomException(
CustomExceptionType.SYSTEM_ERROR, "系统出现未知异常,请联系管理员!"));
}
}