/**
* @ControllerAdvice + @ExceptionHandler 处理controller抛出的异常。
*/
@RestControllerAdvice
@Slf4j
public class RestExceptionHandler {
/**
* 监控 Exception
*/
@ExceptionHandler(value = Exception.class)
public DataResult exception(Exception e){
//打印日志,并返回相关信息给前端
log.error("Exception,{}",e.getLocalizedMessage(),e);
return DataResult.getResult(BaseResponseCode.SYSTEM_ERROR);
}
/**
* 监控 自定义的异常
*/
@ExceptionHandler(value = BusinessException.class)
public DataResult businessException(BusinessException e){
log.error("businessException,{}",e.getLocalizedMessage(),e);
return DataResult.getResult(e.getCode(),e.getDefaultMessage());
}
/**
* 处理validation 框架异常【如果不单独监控处理,就会进入@ExceptionHandler(value = Exception.class),异常信息提示的就不准确】
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
<T> DataResult<T> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
//日志输出
log.error("methodArgumentNotValidExceptionHandler bindingResult.allErrors():{},exception:{}", e.getBindingResult().getAllErrors(), e);
//拿到所有异常【有可能多个异常】
List<ObjectError> errors = e.getBindingResult().getAllErrors();
return createValidExceptionResp(errors);
}
private <T> DataResult<T> createValidExceptionResp(List<ObjectError> errors) {
String[] msgs = new String[errors.size()];
int i = 0;
//遍历每个异常
for (ObjectError error : errors) {
//拿到defaultMessage【VO中通过Hibernate-Validator框架 校验的注解@NotNull提示信息(message = "age 不能为空")存放在DefaultMessage中】
msgs[i] = error.getDefaultMessage();
log.info("msg={}",msgs[i]);
i++;
}
//如果多个校验条件都不满足,拿出第一个提示给前端就OK
return DataResult.getResult(BaseResponseCode.METHOD_IDENTITY_ERROR.getCode(), msgs[0]);
}
@ExceptionHandler(UnauthorizedException.class)
public DataResult unauthorizedException(UnauthorizedException e){
log.error("UnauthorizedException,{},{}",e.getLocalizedMessage(),e);
return DataResult.getResult(BaseResponseCode.NOT_PERMISSION);
}
}
/**
* 自定义运行时异常,手动抛出的业务异常类
*/
@Getter
public class BusinessException extends RuntimeException{
/**
* 异常 code
*/
private final int code;
/**
* 异常提示
*/
public final String defaultMessage;
public BusinessException(int code, String defaultMessage) {
//RuntimeException 有时要传入一个message
super(defaultMessage);
this.code = code;
this.defaultMessage = defaultMessage;
}
/**
* 我们会在复杂的带有数据库事务的业务中,经常遇到一些不规则的信息,这个就需要我们后端根据相应的业务
* 抛出相应的运行时异常,进行数据库事务回滚,并希望该异常信息能被返回显示给用户。
* @param baseResponseCode
*/
public BusinessException(BaseResponseCode baseResponseCode){
this(baseResponseCode.getCode(), baseResponseCode.getMsg());
}
}
---无法捕获处理的异常--
@Slf4j
public class CustomAccessControlerFilter extends AccessControlFilter {
/**
* 当不允许通过的时候。
* @return true:表示自己不处理,进入下一个链式调用
* false:表示自己已经处理了(比如重定向到另一个页面)
*/
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) {
HttpServletRequest request= (HttpServletRequest) servletRequest;
try {
String accessToken=request.getHeader(Constant.ACCESS_TOKEN);
if(StringUtils.isEmpty(accessToken)){
//如果没有携带accessToken,抛出异常,响应给客户端。但这里抛出的异常,在全局异常中监控不到,所以我们要自己try-catch处理
throw new BusinessException(BaseResponseCode.TOKEN_NOT_NULL);
}
CustomUsernamePasswordToken customUsernamePasswordToken=new CustomUsernamePasswordToken(accessToken);
this.getSubject(servletRequest,servletResponse).login(customUsernamePasswordToken);
} catch (BusinessException e) {
//捕获异常后的异常处理方式
customRsponse(e.getCode(),e.getDefaultMessage(),servletResponse);
return false;
} catch (AuthenticationException e) {
if(e.getCause() instanceof BusinessException){
//主动抛出的异常
BusinessException exception= (BusinessException) e.getCause();
customRsponse(exception.getCode(),exception.getDefaultMessage(),servletResponse);
}else {
//系统抛出的异常
customRsponse(BaseResponseCode.SHIRO_AUTHENTICATION_ERROR.getCode(),BaseResponseCode.SHIRO_AUTHENTICATION_ERROR.getMsg(),servletResponse);
}
return false;
}
return true;
}
/**
* 自定义错误响应
*/
private void customRsponse(int code, String msg, ServletResponse response){
// 用DataResult封装异常信息,然后直接写个流OutputStream,返回给客户端相应的JSON格式的信息
try {
DataResult result=DataResult.getResult(code,msg);
response.setContentType("application/json; charset=utf-8");
response.setCharacterEncoding("UTF-8");
//将DataResult转为json
String userJson = JSON.toJSONString(result);
//Servlet取得输出流对象
OutputStream out = response.getOutputStream();
//json 转为byte数组,并写入输出流对象
out.write(userJson.getBytes("UTF-8"));
//清空缓冲区的数据流
out.flush();
} catch (IOException e) {
log.error("eror={}",e);
}
}
}