该统一异常处理基于springboot架构,利用切面技术,在service层或者controller层抛出异常后,被控制层切面捕获,转换后统一返回
1、切面:控制层异常类,HandlerExceptionAspect.java
/**
* 控制层异常切面
*/
@Slf4j
@ControllerAdvice
public class HandlerExceptionAspect {
/**
* 统一处理JSON响应结果的错误结果 例如:{"code":201,message:"入参异常",data:null}
*
* @param cause {@link Throwable} 异常
* @param request {@link HttpServletRequest} 请求
* @return {@link CommonResult} 响应结果
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public CommonResult process(Throwable cause, HttpServletRequest request, HttpServletResponse response) {
CommonResult result;
if (cause instanceof WyjmRPCException) {
result = new CommonResult(((WyjmRPCException) cause).getCode(), cause.getMessage());
} else if (cause instanceof MethodArgumentNotValidException) {
String errorMessage = this.methodArgumentMessage((MethodArgumentNotValidException) cause);
result = new CommonResult(CodeEnum.INCORRECT_REQ_PARAM.getCode(), errorMessage);
} else if (cause instanceof SingleParamException) {
String errorMessage = CodeEnum.getMessageByCode(Integer.valueOf(cause.getMessage()));
result = new CommonResult(CodeEnum.INCORRECT_REQ_PARAM.getCode(), errorMessage);
//等等异常分类
} else {
result = new CommonResult(CodeEnum.UNKOWN_ERROR.getCode(), CodeEnum.UNKOWN_ERROR.getMessage());
}
this.setHeader(request, response);
log.error("error:{}", cause);
return result;
}
/**
* 参数异常消息获取
*
* @param cause 参数异常
* @return 异常原因
*/
private String methodArgumentMessage(MethodArgumentNotValidException cause) {
MethodArgumentNotValidException validException = cause;
BindingResult bindingResult = validException.getBindingResult();
List<ObjectError> allErrors = bindingResult.getAllErrors();
StringBuilder builder = new StringBuilder();
for (ObjectError objectError : allErrors) {
if (StringUtils.isNotBlank(objectError.getDefaultMessage())) {
if (StringUtils.isNotBlank(builder)) {
builder.append(";");
}
builder.append(objectError.getDefaultMessage());
}
}
if (StringUtils.isBlank(builder)) {
builder.append(CodeEnum.INCORRECT_REQ_PARAM.getMessage());
}
return builder.toString();
}
/**
* 响应头部跨域设置
* @param request
* @param response
*/
private void setHeader(HttpServletRequest request, HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
}
}
2、异常基类:WyjmException.java,继承RuntimeException异常类
/**
* 通用异常
*
*/
@ToString
public class WyjmException extends RuntimeException {
private static final long serialVersionUID = 111820440373615072L;
/**
* 异常代码
*/
private int code;
/**
* 异常消息
*/
private String message;
public WyjmException(CodeEnum codeEnum) {
this.code = codeEnum.getCode();
this.message = codeEnum.getMessage();
}
public WyjmException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}
public WyjmException(int code, String message, Throwable cause) {
super(message, cause);
this.code = code;
this.message = message;
}
//code、message的get\set方法
}
3、具体抛出异常类:WyjmRPCException、WyjmServiceException等代码类似,用来区别该异常是在哪个地方抛出,
public class WyjmRPCException extends WyjmException implements Serializable{
public WyjmRPCException(CodeEnum codeEnum) {
super(codeEnum);
}
public WyjmRPCException(int code, String message) {
super(code, message);
}
public WyjmRPCException(int code, String message, Throwable cause) {
super(code, message, cause);
}
}
/**
* 方法参数验证异常
*/
public class SingleParamException extends ServletRequestBindingException {
/**
* 异常结构
* @param msg
*/
public SingleParamException(String msg) {
super(StringUtils.isEmpty(msg) ? "999" : msg);
}
}
4、枚举类:CodeEnum
/**
* 应答码枚举类
*
* @create 2018-8-21
*/
public enum CodeEnum {
/**
* 未登陆
*/
USER_NOT_LOGIN(100, "未登陆"),
/**
* 未注册
*/
USER_NOT_REGISTER(101, "用户未注册")
//等等
/**
* 响应代码
*/
private final int code;
/**
* 响应消息
*/
private final String message;
CodeEnum(int _code, String _message) {
this.code = _code;
this.message = _message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
/**
* 通过枚举code获取对应的message
*
* @return 取不到时返回null
*/
public static String getMessageByCode(int code) {
for (CodeEnum _enum : values()) {
if (_enum.getCode() == code) {
return _enum.getMessage();
}
}
return null;
}
/**
* 通过枚举code获取枚举对象
*
* @return 取不到时返回null
*/
public static CodeEnum getByCode(int code) {
for (CodeEnum _enum : values()) {
if (_enum.getCode() == code) {
return _enum;
}
}
return null;
}
}
5、统一返回包装类:CommonResult
/**
* 响应结果
*/
@Data
public class CommonResult<T> {
/**
* 应答码
*
* @see 默认值为操作成功
*/
private int code = CodeEnum.SUCCESS.getCode();
/**
* 应答码描述
*
* @see 默认值为操作成功
*/
private String message = CodeEnum.SUCCESS.getMessage();
/**
* 应答数据体
*
* @see 可以为空
*/
private T data;
public CommonResult() {
this(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMessage(), null);
}
public CommonResult(CodeEnum codeEnum) {
this(codeEnum.getCode(), codeEnum.getMessage(), null);
}
/**
* 默认返回的code=CodeEnum.SUCCESS.getCode()
*/
public CommonResult(T data) {
this(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMessage(), data);
}
/**
* 默认返回的data=null
*/
public CommonResult(int code, String message) {
this(code, message, null);
}
public CommonResult(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
/** 判断返回结果是否成功 */
public boolean success() {
return code == CodeEnum.SUCCESS.getCode();
}
}
6、在service层使用:
@Slf4j
@Service
public class ProductCategoryServiceImpl implements ProductCategoryService {
@Override
public List<ProductCategoryResVO> getCategories(ProductCategoryReqVO req) {
ProductJSFResult<List<CategoryResponseDTO>> result =
categoryJSFService.searchCategories(req.getParentId(), ProductConstants.CATEGORY_LEVEL_ALL_NODE);
if (!result.success()) {
log.error("根据parentId获取分类数据失败 code={} msg={}", result.getCode(), result.getMessage());
throw new WyjmException(CodeEnum.NOT_DATA);
}
log.info("根据parentId获取分类数据 parentId={} result={}", req.getParentId(), JSON.toJSONString(result.getData()));
return transform(result.getData(), req.getParentId() == 0 ? Boolean.TRUE : Boolean.FALSE, req.getParentId());
}