1. 新建ApiResult作为通用返回对象
// package vo
@Data
public class ApiResult<T> implements Serializable
{
/**
* 状态码
*/
private Integer code;
/**
* 错误码
*/
private String errorCode;
/**
* 错误描述
*/
private String errorDesc;
/**
* 数据
*/
private T data;
public ApiResult(Integer code, String errorCode, String errorDesc, T data) {
this.code = code;
this.errorCode = errorCode;
this.errorDesc = errorDesc;
this.data = data;
}
public static <T> ApiResult<T> success() {
return new ApiResult<T>(0, null, null, null);
}
public static <T> ApiResult<T> success(T data) {
return new ApiResult<T>(0, null, null, data);
}
public static <T> ApiResult<T> error(String errorCode, String errorDesc) {
return new ApiResult<T>(1, errorCode, errorDesc, null);
}
public static <T> ApiResult<T> error(String errorCode, String errorDesc, T data) {
return new ApiResult<T>(1, errorCode, errorDesc, data);
}
}
2. 新建错误枚举类
// package exception
public enum CommonErrorEnum
{ // 10000通用错误
UNKNOWN_ERROR("BYSJ-10001","未知错误"),
PARAMETER_VALIDATION_ERROR("BYSJ-10002","参数不合法"),
ADD_FAIL("BYSJ-10004","添加失败"),
DELETE_FAIL("BYSJ-10005","删除失败"),
UPDATE_FAIL("BYSJ-10006","更新失败"),
FIND_FAIL("BYSJ-10007","资源不存在"),
;
// 错误码
private String errorCode;
// 错误描述
private String errorDesc;
CommonErrorEnum(String errorCode, String errorDesc) {
this.errorCode = errorCode;
this.errorDesc = errorDesc;
}
public String getErrorCode() {
return errorCode;
}
public String getErrorDesc() {
return errorDesc;
}
// 自定义错误描述
public CommonErrorEnum setErrorDesc(String errorDesc) {
this.errorDesc = errorDesc;
return this;
}
public BusinessException getBusinessException(){
return new BusinessException(this);
}
}
3. 自定义异常类
// package exception
class BusinessException extends RuntimeException
{
private CommonErrorEnum commonErrorEnum;
BusinessException(CommonErrorEnum commonErrorEnum) {
super();
this.commonErrorEnum = commonErrorEnum;
}
String getErrorCode() {
return commonErrorEnum.getErrorCode();
}
String getErrorDesc() {
return commonErrorEnum.getErrorDesc();
}
}
自定义异常继承RuntimeException
而不是Exception
是因为运行时异常不需要捕获,也不需要做对应处理。具体继承谁还是根据业务来,这里不多概述。
4. 新建全局异常类
// package exception
@RestControllerAdvice(basePackages = "com.hbue.bysj.*", annotations = Controller.class)
public class GlobalExceptionHandler
{
/**
* 捕获业务异常
* @param ex
* @return
*/
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult handleBusinessException(Exception ex) {
BusinessException businessException = (BusinessException)ex;
return ApiResult.error(businessException.getErrorCode(), businessException.getErrorDesc());
}
/**
* 捕获其他异常
* @param ex
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult handleException(Exception ex) {
ex.printStackTrace();
return ApiResult.error(CommonErrorEnum.UNKNOWN_ERROR.getErrorCode(), CommonErrorEnum.UNKNOWN_ERROR.getErrorDesc());
}
}
全局异常类中可以捕获多种异常,并做对应的处理,建议分开写,不要直接用Exception
为条件捕获所有异常,然后再使用instanceof
来判断具体的异常类型。
补充:对于某些不处于Controller层的异常,例如文件尺寸过大异常,就不能被Controller捕获器捕获,可以用下面的方法捕获:
@RestControllerAdvice
public class FileSizeExceptionHandler
{
// 设定上传大小3MB 超出大小捕获异常MaxUploadSizeExceededException
@ExceptionHandler(MaxUploadSizeExceededException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult handleMaxUploadSizeExceededException() {
return ApiResult.error(CommonErrorEnum.FILE_SIZE_ERROR.getErrorCode(), CommonErrorEnum.FILE_SIZE_ERROR.getErrorDesc());
}
}
5. 抛出异常方式
例如想在Service层要抛出一个ADD_FAIL
异常,应该这么写:
throw CommonErrorEnum.ADD_FAIL.getBusinessException();
而不是:
throw new BusinessException(CommonErrorEnum.ADD_FAIL);
第一种写法的好处是开发者能更注重产生的异常类型,而不用在乎抛出具体什么异常。
如果想抛出个ADD_FAIL
异常,但不想使用ADD_FAIL
对应的提示信息怎么办?
throw CommonErrorEnum.ADD_FAIL.setErrorDesc("添加用户信息失败").getBusinessException();
假设现在有多种自定义异常,异常枚举类中就可以这么写:
public BusinessException getBusinessException() {
return new BusinessException(this);
}
// throw CommonErrorEnum.SQL_ASYCN_ERROR.getSqlException();
public SqlException getSqlException() {
return new SqlException(this);
}
...
6. 总结
后端任何地方抛出异常,被全局异常捕获后,就会封装成一个携带有errorCode
和errorDesc
的ApiResult
给前端,http状态码保证是正常的(200)。
然后前端就能通过ApiResult.code
来判断接口是否调用成功,并做相应处理。