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, "系统出现未知异常,请联系管理员!"));
}
}