切面中的通知包括:前置通知(Before advice)/后置通知(After advice)/环绕通知(Around advice)/异常通知(After throwing advice)/正常返回通知(After returning advice)
其中:后置通知与正常返回通知的区别
后置通知不管抛出异常与否都会执行,正常返回通知在连接点正常执行后,没有抛出异常才会执行.
异常通知是在出现异常后才会执行的切面.
直接上异常通知的代码
@Aspect
@Component
public class ControllerExceptionAspect {
private static ObjectMapper objectMapper = new ObjectMapper();
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
private void webPoint(){
}
/**
* 异常通知
* @param e
*/
@AfterThrowing(pointcut = "webPoint()",throwing = "e")
public void handlerException(Exception e){
e.printStackTrace();
writeContent(e.getMessage());
}
/**
* 对异常进行处理,统一捕获返回到前端显示
* @param content
*/
private void writeContent(String content) {
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getResponse();
response.reset();
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "text/plain;charset=UTF-8");
response.setHeader("icop-content-type", "exception");
PrintWriter writer = null;
try {
writer = response.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
MessageDto messageDto = new MessageDto();
messageDto.setCode("400");
messageDto.setMessage(content);
try {
writer.print(objectMapper.writeValueAsString(messageDto));
} catch (JsonProcessingException e) {
e.printStackTrace();
System.out.println("向前端传送错误数据失败!");
}
writer.flush();
writer.close();
}
}
使用该注解可以针对不同的异常进行不同逻辑的处理,上面的方式只能有一种处理逻辑.
@Component
@RestControllerAdvice
@Log4j2
public class GlobalExceptionAdvance {
@Autowired
private CoreProperties coreProperties;
/**
* 400 请求动词错误
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public RequestResult handle(HttpRequestMethodNotSupportedException e) {
return RequestResult.failure(ResultCode.BAD_REQEUST,
"请求动词不受支持,当前为[" + e.getMethod() + "],正确为" + Arrays.toString(e.getSupportedMethods()));
}
/**
* 400 请求Content-Type错误 往往是因为后端的@RequestBody和前端的application/json没有同时指定或同时不指定。
*/
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public RequestResult handle(HttpMediaTypeNotSupportedException e) {
return RequestResult.failure(ResultCode.BAD_REQEUST,
"Content-Type错误,当前为[" + e.getContentType().toString().replace(";charset=UTF-8", "") +
"],正确为[application/json]");
}
/**
* 400 缺少RequestParam参数
*
* 但以下情况不会被捕获:
* /user?type
* /user?type=
*
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public RequestResult handle(MissingServletRequestParameterException e) {
return RequestResult.failure(ResultCode.BAD_REQEUST, "缺少请求参数" + e.getParameterName());
}
/**
* 400 RequestParam参数类型错误
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public RequestResult handle(MethodArgumentTypeMismatchException e) {
return RequestResult.failure(ResultCode.BAD_REQEUST, e.getName() + "类型错误");
}
/**
* 400 RequestParam参数没有通过注解校验(控制器声明@Validated时)
*/
@ExceptionHandler(ConstraintViolationException.class)
public RequestResult handle(ConstraintViolationException e) {
Set> cvs = e.getConstraintViolations();
RequestTrack requestTrack = Requests.getRequestTrack();
String[] paramNames = requestTrack.getParameterNames();
Object[] paramValues = requestTrack.getParameterValues();
List invalids = new ArrayList<>();
for (ConstraintViolation cv : cvs) {
// 参数路径
PathImpl pathImpl = (PathImpl) cv.getPropertyPath();
// 参数下标
int paramIndex = pathImpl.getLeafNode().getParameterIndex();
invalids.add(Invalid.builder().name(paramNames[paramIndex]).value(paramValues[paramIndex]).cause(
cv.getMessage()).build());
}
return RequestResult.failure(ResultCode.BAD_REQEUST, "数据校验未通过").setData(invalids);
}
/**
* 400 RequestParam参数没有通过注解校验(multi-param且声明@Valid时)
*/
@ExceptionHandler(BindException.class)
public RequestResult handle(BindException e) {
List invalids = new ArrayList<>();
for (FieldError fieldError : e.getFieldErrors()) {
invalids.add(Invalid.builder().name(fieldError.getField()).value(fieldError.getRejectedValue())
.cause(fieldError.getDefaultMessage()).build());
}
return RequestResult.failure(ResultCode.BAD_REQEUST, "数据校验未通过").setData(invalids);
}
/**
* 400 请求Body格式错误
*
* 以下情况时,会被捕获:
* alkdjfaldfjlalkajkdklf
* (空)
* {"userAge"="notNumberValue"}
*
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public RequestResult httpMessageNotReadable() {
return RequestResult.failure(ResultCode.BAD_REQEUST, "请求Body不可读。可能是JSON格式错误,或JSON不存在,或类型错误");
}
/**
* 400 请求Body内字段没有通过注解校验(形参声明@Valid时)
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public RequestResult handle(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
List invalids = new ArrayList<>();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
invalids.add(Invalid.builder().name(fieldError.getField()).value(fieldError.getRejectedValue()).cause(
fieldError.getDefaultMessage()).build());
}
return RequestResult.failure(ResultCode.BAD_REQEUST, "数据校验未通过").setData(invalids);
}
/**
* 400 成功绑定并通过校验的请求方法参数,没有通过切面的额外校验(如?temp&foo=这样的情况)
*/
@ExceptionHandler(ExtraInvalidException.class)
public RequestResult handle(ExtraInvalidException e) {
return RequestResult.failure(ResultCode.BAD_REQEUST, "数据校验未通过").setData(e.getInvalids());
}
/**
* 401 未登录
*/
@ExceptionHandler(UnsignedException.class)
public RequestResult handleUnsignException(UnsignedException e) {
return RequestResult.failure(ResultCode.UNSIGNED, e.getMessage());
}
/**
* 403 没权限或是请求actuator时提供的token不正确
*/
@ExceptionHandler(AuthorizationException.class)
public RequestResult handleAuthorizationException() {
return RequestResult.failure(ResultCode.FORBIDDEN);
}
/**
* 404 找不到请求
*/
@ExceptionHandler(RequestNotFoundException.class)
public RequestResult handleRequestNotFoundException() {
return RequestResult.failure(ResultCode.NOT_FOUND);
}
/**
* 1001 业务异常
*/
@ExceptionHandler(ServiceException.class)
public RequestResult handle(ServiceException e) {
return RequestResult.failure(ResultCode.SERVICE_ERROR, e.getMessage());
}
/**
* 500 内部BUG
*
* 控制层上方发生的BUG(如过滤器中的BUG),无法返回标识符。因为没有进入切面
* 控制层下方发生的BUG(如果业务层、持久层的BUG),能够返回标识符
*
*/
@ExceptionHandler(Throwable.class)
public RequestResult handle(Throwable e) {
RequestTrack track = Requests.getRequestTrack();
RequestResult requestResult;
if (track == null) {
log.error("统一异常处理被击穿!", e);
requestResult = RequestResult.failure(ResultCode.INTERNAL_ERROR);
} else {
String insignia = track.getInsignia();
log.error("统一异常处理被击穿!标识:" + insignia, e);
requestResult = RequestResult.failure(ResultCode.INTERNAL_ERROR, "内部错误(" + insignia + ")");
}
// 详细错误信息
if (coreProperties.isDebug()) {
String msg = e.getMessage();
if (msg == null) {
msg = e.getClass().getSimpleName();
}
if (msg.length() > 200) {
msg = msg.substring(0, 200) + " ...";
}
requestResult.setMessage(requestResult.getMessage() + ",详细信息(" + msg + ")");
}
return requestResult;
}
}