场景:在日常代码中,后端同学与前端同学交互过程中,异常的返回总是要遵循一套规定。后端的调用不同的rpc接口,异常的反馈总是不一样,所以要统一处理,下面是比较通用的模式:采用@ControllerAdvice
异常通用处理方法:
import com.dianping.credit.audit.disposal.exception.DisposeException;
import com.dianping.credit.audit.disposal.web.enums.ResponseEnum;
import com.dianping.credit.audit.disposal.web.vo.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* 全局异常处理
*/
@Slf4j
@ControllerAdvice
public class WebExceptionHandler {
@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public Response handleException(Exception e) {
log.error("handleException Internal exception:", e);
return Response.getFail(ResponseEnum.ERROR);
}
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
public Response handleArgumentException(MethodArgumentNotValidException e) {
FieldError error = e.getBindingResult().getFieldError();
String message = error != null ? error.getField() + ": " + error.getDefaultMessage() : null;
log.error("MethodArgumentNotValidException:",e);
return Response.getFail(ResponseEnum.INVALID_PARAM.getCode(), message);
}
@ResponseBody
@ExceptionHandler({IllegalArgumentException.class})
public Response handleIllegalArgumentException(IllegalArgumentException e) {
log.error("handleIllegalArgumentException:",e);
return Response.getFail(ResponseEnum.INVALID_PARAM);
}
/**
* 自定义异常
* @param e
* @return
*/
@ResponseBody
@ExceptionHandler({DisposeException.class})
public Response handleDisposeException(DisposeException e) {
log.error("DisposeException:",e);
return Response.getFail(e.getCode(),e.getMessage());
}
}
自定义异常:
import lombok.Data;
import java.io.Serializable;
/**
* 处置平台异常
* @Author: dainan
* @Date: 2019/9/18 17:11
* @Description:
*/
@Data
public class DisposeException extends RuntimeException implements Serializable {
private int code;
public DisposeException(String message) {
super(message);
}
public DisposeException(int code, String message) {
super(message);
this.code = code;
}
public static DisposeException of(Throwable t){
return new DisposeException(t.getMessage());
}
@Override
public String toString() {
return String.format("DisposalException[code=%s,message=%s]", code, super.getMessage());
}
}
常见异常code自定义:
import java.io.Serializable;
/**
* @Author: dainan
* @Date: 2019/12/27 17:38
* @Description:
*/
public class DisposeExceptionCode implements Serializable {
/**
* 成功
*/
public static final int SUCCESS = 10000;
/**
* 系统错误
*/
public static final int SYSTEM_ERROR = 10001;
/**
* 服务超时
*/
public static final int TIMEOUT = 10002;
/**
* 服务限流
*/
public static final int SERVICE_LIMIT = 10003;
/**
* 参数错误
*/
public static final int ERROR_PARAMETER = 10004;
/**
* 接口不存在
*/
public static final int INTEFACE_NOT_EXIST = 10005;
/**
* HTTP METHOD不支持
*/
public static final int HTTP_NOT_SUPPORT = 10006;
/**
* 非法请求
*/
public static final int ILLEGAL_REQUEST = 10007;
/**
* 非法用户
*/
public static final int ILLEGAL_USER = 10008;
/**
* 没有权限
*/
public static final int NO_PERMISSION = 10009;
}
在某些情况下全局异常处理没有成功:
解决办法:
1.确保注解@RestControllerAdvice/@ControllerAdvice的类被spring容器管理到。
①spring boot Java配置检查@SpringBootApplication(scanBasePackages = )(scanBasePackages 配置的包是否包含这个类默认情况下spring boot项目扫描的是@SpringBootApplication注解所在类的包及子包)
② xml配置的spring 普通项目检查<context:component-scan base-package="com.test"/>
2.检查项目中所有的切面编程,是否在某个切面将异常try-catch然后没有扔出来。常见的就是切面的环绕处理,捕获了异常忘记抛出来。
楼主碰到了这个问题:
@Around("beforePointcut()")
public Object doAccessCheck(ProceedingJoinPoint joinPoint) throws Throwable {
Object object;
UserInfo userInfo = getUserInfoFromSso(joinPoint.getArgs());
if (userInfo == null) {
return ConstantUtil.paraError(StatusCodeEnum.AUTH_FAIL);
}
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
setUserInfo(args[0], userInfo);
}
try {
object = joinPoint.proceed();
daDian(joinPoint, userInfo.getRealName(), userInfo.getLoginName(), object);
} catch (Throwable e) {
LOGGER.error("【" + joinPoint.getSignature() + "方法发生异常】" + "【异常报告:" + e.getCause() + "】", e);
throw e;//如果这个地方没有抛出,就没法捕获这个异常,全局异常处理就不会有效果
}
return object;
}
3.检查项目中是否有其他的相同的全局异常处理类,例如BaseController中是否已经定义了