响应信息封装
响应码设置
响应信息中的状态码不要与http状态码重复,可以自定义响应码,可以根据不同业务设置对应规则的响应码。
例如:
/**
* 错误码<br>
* 一共分为三种: 1)系统错误、2)用户级别错误、3)未预期到的错误
*
*/
public interface ErrorCode {
/**
* 系统等级
*/
String LEVEL_SYSTEM = "system";
/**
* 用户等级
*/
String LEVEL_USER = "user";
/**
* 未预期到的等级
*/
String LEVEL_UNEXPECTED = "unexpected";
/**
* 错误码
*/
int getCode();
/**
* 错误消息
*
*/
String getMsg();
/**
* 错误等级
*/
String getLevel();
}
实现对应的错误码
@Getter
@AllArgsConstructor
public enum SystemErrorCode implements ErrorCode {
/**
* 系统错误
*/
SYSTEM_ERROR(10001, "系统似乎出现了点小问题"),
;
private final int code;
private final String msg;
private final String level;
SystemErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
this.level = LEVEL_SYSTEM;
}
}
编码可以是数字也可以是字符
@AllArgsConstructor
@NoArgsConstructor
public enum ResultCode implements IResultCode, Serializable {
SUCCESS("00000", "一切ok"),
USER_ERROR("A0001", "用户端错误"),
USER_LOGIN_ERROR("A0200", "用户登录异常"),
USER_NOT_EXIST("A0201", "用户不存在"),
USER_ACCOUNT_LOCKED("A0202", "用户账户被冻结"),
USER_ACCOUNT_INVALID("A0203", "用户账户已作废"),
USERNAME_OR_PASSWORD_ERROR("A0210", "用户名或密码错误"),
PASSWORD_ENTER_EXCEED_LIMIT("A0211", "用户输入密码次数超限"),
CLIENT_AUTHENTICATION_FAILED("A0212", "客户端认证失败"),
TOKEN_INVALID("A0230", "token无效或已过期"),
TOKEN_ACCESS_FORBIDDEN("A0231", "token已被禁止访问"),
AUTHORIZED_ERROR("A0300", "访问权限异常"),
ACCESS_UNAUTHORIZED("A0301", "访问未授权"),
FORBIDDEN_OPERATION("A0302", "演示环境禁止新增、修改和删除重要数据,请本地部署后测试"),
REPEAT_SUBMIT_ERROR("A0303", "您的请求已提交,请不要重复提交或等待片刻再尝试。"),
PARAM_ERROR("A0400", "用户请求参数错误"),
RESOURCE_NOT_FOUND("A0401", "请求资源不存在"),
PARAM_IS_NULL("A0410", "请求必填参数为空"),
USER_UPLOAD_FILE_ERROR("A0700", "用户上传文件异常"),
USER_UPLOAD_FILE_TYPE_NOT_MATCH("A0701", "用户上传文件类型不匹配"),
USER_UPLOAD_FILE_SIZE_EXCEEDS("A0702", "用户上传文件太大"),
USER_UPLOAD_IMAGE_SIZE_EXCEEDS("A0703", "用户上传图片太大"),
SYSTEM_EXECUTION_ERROR("B0001", "系统执行出错"),
SYSTEM_EXECUTION_TIMEOUT("B0100", "系统执行超时"),
SYSTEM_ORDER_PROCESSING_TIMEOUT("B0100", "系统订单处理超时"),
SYSTEM_DISASTER_RECOVERY_TRIGGER("B0200", "系统容灾功能被触发"),
FLOW_LIMITING("B0210", "系统限流"),
DEGRADATION("B0220", "系统功能降级"),
SYSTEM_RESOURCE_ERROR("B0300", "系统资源异常"),
SYSTEM_RESOURCE_EXHAUSTION("B0310", "系统资源耗尽"),
SYSTEM_RESOURCE_ACCESS_ERROR("B0320", "系统资源访问异常"),
SYSTEM_READ_DISK_FILE_ERROR("B0321", "系统读取磁盘文件失败"),
CALL_THIRD_PARTY_SERVICE_ERROR("C0001", "调用第三方服务出错"),
MIDDLEWARE_SERVICE_ERROR("C0100", "中间件服务出错"),
INTERFACE_NOT_EXIST("C0113", "接口不存在"),
MESSAGE_SERVICE_ERROR("C0120", "消息服务出错"),
MESSAGE_DELIVERY_ERROR("C0121", "消息投递出错"),
MESSAGE_CONSUMPTION_ERROR("C0122", "消息消费出错"),
MESSAGE_SUBSCRIPTION_ERROR("C0123", "消息订阅出错"),
MESSAGE_GROUP_NOT_FOUND("C0124", "消息分组未查到"),
DATABASE_ERROR("C0300", "数据库服务出错"),
DATABASE_TABLE_NOT_EXIST("C0311", "表不存在"),
DATABASE_COLUMN_NOT_EXIST("C0312", "列不存在"),
DATABASE_DUPLICATE_COLUMN_NAME("C0321", "多表关联中存在多个相同名称的列"),
DATABASE_DEADLOCK("C0331", "数据库死锁"),
DATABASE_PRIMARY_KEY_CONFLICT("C0341", "主键冲突");
@Override
public String getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
private String code;
private String msg;
@Override
public String toString() {
return "{" +
"\"code\":\"" + code + '\"' +
", \"msg\":\"" + msg + '\"' +
'}';
}
public static ResultCode getValue(String code){
for (ResultCode value : values()) {
if (value.getCode().equals(code)) {
return value;
}
}
return SYSTEM_EXECUTION_ERROR; // 默认系统执行错误
}
}
响应信息封装
public class ResponseDTO<T> {
public static final int OK_CODE = 0;
public static final String OK_MSG = "操作成功";
@Schema(description = "返回码")
private Integer code;
@Schema(description = "级别")
private String level;
private String msg;
private Boolean ok;
@Schema(description = "返回数据")
private T data;
@SchemaEnum(value = DataTypeEnum.class,desc = "数据类型")
private Integer dataType;
public ResponseDTO(Integer code, String level, boolean ok, String msg, T data) {
this.code = code;
this.level = level;
this.ok = ok;
this.msg = msg;
this.data = data;
this.dataType = DataTypeEnum.NORMAL.getValue();
}
public ResponseDTO(Integer code, String level, boolean ok, String msg) {
this.code = code;
this.level = level;
this.ok = ok;
this.msg = msg;
this.dataType = DataTypeEnum.NORMAL.getValue();
}
public ResponseDTO(ErrorCode errorCode, boolean ok, String msg, T data) {
this.code = errorCode.getCode();
this.level = errorCode.getLevel();
this.ok = ok;
if (StringUtils.isNotBlank(msg)) {
this.msg = msg;
} else {
this.msg = errorCode.getMsg();
}
this.data = data;
this.dataType = DataTypeEnum.NORMAL.getValue();
}
public static <T> ResponseDTO<T> ok() {
return new ResponseDTO<>(OK_CODE, null, true, OK_MSG, null);
}
public static <T> ResponseDTO<T> ok(T data) {
return new ResponseDTO<>(OK_CODE, null, true, OK_MSG, data);
}
public static <T> ResponseDTO<T> okMsg(String msg) {
return new ResponseDTO<>(OK_CODE, null, true, msg, null);
}
// -------------------------------------------- 最常用的 用户参数 错误码 --------------------------------------------
public static <T> ResponseDTO<T> userErrorParam() {
return new ResponseDTO<>(UserErrorCode.PARAM_ERROR, false, null, null);
}
public static <T> ResponseDTO<T> userErrorParam(String msg) {
return new ResponseDTO<>(UserErrorCode.PARAM_ERROR, false, msg, null);
}
// -------------------------------------------- 错误码 --------------------------------------------
public static <T> ResponseDTO<T> error(ErrorCode errorCode) {
return new ResponseDTO<>(errorCode, false, null, null);
}
public static <T> ResponseDTO<T> error(ErrorCode errorCode, boolean ok) {
return new ResponseDTO<>(errorCode, ok, null, null);
}
public static <T> ResponseDTO<T> error(ResponseDTO<?> responseDTO) {
return new ResponseDTO<>(responseDTO.getCode(), responseDTO.getLevel(), responseDTO.getOk(), responseDTO.getMsg(), null);
}
public static <T> ResponseDTO<T> error(ErrorCode errorCode, String msg) {
return new ResponseDTO<>(errorCode, false, msg, null);
}
public static <T> ResponseDTO<T> errorData(ErrorCode errorCode, T data) {
return new ResponseDTO<>(errorCode, false, null, data);
}
}
统一异常处理
定义自定义业务异常
/**
* 基础业务异常类,所有业务异常需继承此类
*/
public class BusinessException extends RuntimeException {
// 错误码
private final int code;
// 附加数据(如参数错误详情)
private final Object data;
// 构造方法(重载,满足不同场景)
public BusinessException(int code, String message) {
super(message);
this.code = code;
this.data = null;
}
public BusinessException(int code, String message, Object data) {
super(message);
this.code = code;
this.data = data;
}
// Getter(无Setter,确保不可变)
public int getCode() { return code; }
public Object getData() { return data; }
}
统一异常处理
例子:
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public <T> Result<T> processException(BindException e) {
log.error("BindException:{}", e.getMessage());
String msg = e.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";"));
return Result.failed(ResultCode.PARAM_ERROR, msg);
}
/**
* RequestParam参数的校验
*
* @param e
* @param <T>
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public <T> Result<T> processException(ConstraintViolationException e) {
log.error("ConstraintViolationException:{}", e.getMessage());
String msg = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";"));
return Result.failed(ResultCode.PARAM_ERROR, msg);
}
}
统一异常处理http状态码
统一异常处理http状态码不要都是200,需要根据业务和标准进行响应
http状态码可以根据封装的信息中封装http_code进行响应,也可以通过异常类型绑定状态码使用@ResponseStatus进行响应
例如:
@ExceptionHandler(BusinessException.class)
public Result<?> handleBusinessException(BusinessException e, HttpServletResponse response) {
log.error("业务异常: {}", e.getMessage());
// 设置HTTP响应状态码
response.setStatus(e.getHttpCode());
return Result.failed(e.getCode(),e.getData());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public <T> Result<T> processException(BindException e) {
log.error("BindException:{}", e.getMessage());
String msg = e.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";"));
return Result.failed(ResultCode.PARAM_ERROR, msg);
}
Comments