一、前言
1、不規範的錯誤碼有什麼問題?
1)了解困難
描述:如果錯誤碼的命名或描述不清晰,可能導緻其他開發人員難以了解其含義。
舉例:例如,一個錯誤碼命名為“ERR1001”,沒有進一步的注釋或描述,可能導緻其他開發人員不知道這個錯誤碼代表的具體問題。
2)不一緻性
描述: 如果錯誤碼的命名、描述或分類不統一,可能導緻代碼的可讀性和可維護性降低。
舉例:例如,有的錯誤碼使用三位數,有的使用兩位數;有的錯誤碼描述具體的問題,而有的描述則較為模糊。
3)排查困難
描述:如果錯誤碼沒有清晰的命名和描述,可能使得調試過程變得困難。
舉例:當出現問題時,開發人員需要檢視大量的日志或代碼來定位問題所在。
4)備援和重複
描述:如果錯誤碼過多或過于複雜,可能導緻代碼中的錯誤處理邏輯變得備援和重複。
舉例:同一個錯誤可能在不同地方有不同的錯誤碼,導緻處理邏輯重複。
5)擴充性差
描述:如果錯誤碼已經定義但後來需要添加新的錯誤碼,可能需要修改多個地方的代碼,增加了維護成本。
2、規範的錯誤碼那麼好,為什麼不規範使用呢?
1)缺乏規範和标準:
在某些情況下,可能沒有明确的規範或标準來指導如何使用錯誤碼。這可能導緻開發人員根據自己的了解和習慣來定義錯誤碼,進而導緻不規範的情況。
2)缺乏意識和經驗:
某些開發人員可能沒有意識到錯誤碼規範化的重要性,或者缺乏足夠的經驗來正确地設計和使用錯誤碼。
3)曆史遺留問題:
在某些項目中,錯誤碼可能已經使用了很長時間,而且已經成為了代碼的一部分。在這種情況下,重新規範化錯誤碼可能會涉及到大量的代碼修改和測試,這可能會被視為成本較高。
4)個人習慣和偏好:
某些開發人員可能更傾向于按照自己的習慣和偏好來使用錯誤碼,而不是遵循團隊的規範。這可能會導緻代碼中的錯誤碼使用不一緻。
3、那怎麼規範錯誤碼呢
1)制定規範和标準:
團隊可以制定明确的規範和标準,指導如何使用錯誤碼,并将其納入代碼審查和開發流程中。
2)教育訓練和指導:
為新開發人員提供教育訓練和指導,使其了解如何正确地設計和使用錯誤碼。
3)重構和改進:
對于曆史遺留問題,可以通過逐漸重構和改進的方式來規範化錯誤碼的使用。
4)代碼審查和團隊協同:
通過代碼審查和團隊協同來確定錯誤碼的規範化和一緻性。
二、規範錯誤碼
1、錯誤碼-分片區
根據号段區分錯誤類型,這裡長度定義了5位,可以根據自己系統規模調整長度
錯誤碼 | 描述 |
00000 | 成功 |
10000 | 參數錯誤 |
20000 | 業務處理失敗(業務上給使用者吐出) |
30000 | RPC處理失敗 --->> 系統_失敗分類(請求0、傳回1)_業務_方法_調用方CODE碼(代碼補齊),極端情況下吐出 99999 |
40000 | 運作處理失敗:一般内部處理使用,極端情況下吐出 99999 |
99999 | 系統太火爆了,請稍後重試! -->極端情況下才吐出 |
@Getter
@AllArgsConstructor
enum ErrorCodeEnum implements CodeEnum {
ERROR_CODE_SUCCESS("00000", "成功"),
ERROR_CODE_PARAMS_ERROR("10000", "參數錯誤"),
ERROR_CODE_BUSINESS_ERROR("20000", "業務處理失敗"),
ERROR_CODE_PRC_ERROR("30000", "RPC處理失敗"),
ERROR_CODE_RUNTIME_ERROR("40000", "運作時失敗"),
ERROR_CODE_FAIL("99999", "系統太火爆了,請稍後重試!"),
;
private final String code;
private final String msg;
}
2、10000-參數異常
非常簡單,直接吐出即可
參數 | 說明 |
code | 錯誤碼 |
msg | 傳回錯誤資訊 |
@Getter
@AllArgsConstructor
enum ParamsErrorEnum implements CodeEnum {
ERROR_CODE_10000("10000", "參數錯誤"),
ERROR_CODE_10001("10001", "不支援的請求方式"),
ERROR_CODE_10002("10002", "參數格式異常"),
;
private final String code;
private final String msg;
}
3、20000-業務異常
參數 | 說明 |
code | 錯誤碼 |
msg | 底層-錯誤資訊 |
showMsg | 吐出-錯誤資訊 |
@Getter
@AllArgsConstructor
enum BusinessErrorEnum implements CodeEnum {
ERROR_CODE_20000("20000", "業務處理失敗", "系統太火爆了,請稍後重試!"),
ERROR_CODE_20001("20001", "訂單建立失敗", "您有一個訂單正在建立,請稍後檢視"),
ERROR_CODE_20002("20002", "付款失敗,存在建立中的訂單", "您的訂單付款失敗,請稍後檢視"),
ERROR_CODE_20003("20003", "付款失敗,存在未支付的訂單", "您的訂單付款失敗,請稍後檢視"),
;
private final String code;
private final String msg;
private final String showMsg;
}
4、30000-RPC異常
該異常一定要合理使用,這樣會讓微服務直接錯誤資訊更明确
說明:系統_失敗分類(請求0、傳回1)_業務_調用方法_調用方CODE碼(代碼補齊)
字段 | 說明 |
系統 | 調用系統:如使用者系統 User |
失敗分類 | 請求失敗:0;傳回失敗:1 |
業務 | 業務(3開頭):使用者資訊業務:30001 |
調用方法 | 業務調用方法(指定一個數字,確定是該業務唯一的方法) |
調用方Code碼 | 後續代碼中補齊,具體看異常抛出錯誤碼使用 |
參數 | 說明 |
code | 錯誤碼:系統_失敗分類(請求0、傳回1)_業務_方法_調用方CODE碼(代碼補齊) |
msg | 底層-錯誤資訊 |
@Getter
@AllArgsConstructor
enum RpcErrorEnum implements CodeEnum {
/**
* 系統_失敗分類(請求0、傳回1)_業務_方法_調用方CODE碼(代碼補齊)
*/
ERROR_CODE_USER_0_30001_0001("USER_0_30001_0001", "RPC異常-USER-使用者資訊-查詢單個使用者資訊-接口調用失敗"),
ERROR_CODE_USER_1_30001_0001("USER_1_30001_0001", "RPC異常-USER-使用者資訊-查詢單個使用者資訊-接口傳回失敗"),
ERROR_CODE_USER_1_30001_0002("USER_1_30001_0002", "RPC異常-USER-使用者資訊-分頁查詢使用者資訊-接口傳回失敗"),
;
private final String code;
private final String msg;
}
5、40000-運作異常
@Getter
@AllArgsConstructor
enum PlatformErrorEnum implements CodeEnum {
ERROR_CODE_40000("40000", "運作時失敗"),
ERROR_CODE_40001("40001", "路由消息處理失敗"),
;
private final String code;
private final String msg;
}
三、錯誤碼使用
1、10000-參數異常
構造器 | 說明 |
ParameterException(CodeEnum.ParamsErrorEnum paramErrorEnum) | 建議使用:傳入一個固定的枚舉值 |
ParameterException(String message) | 不推薦:傳入一個錯誤資訊 |
@Getter
public class ParameterException extends RuntimeException {
/**
* serialVersionUID
*/
private static final long serialVersionUID = -6114625076221233075L;
/**
* 傳回錯誤碼
*/
private final String code;
/**
* BusinessException
*
* @param paramErrorEnum paramErrorEnum
*/
public ParameterException(CodeEnum.ParamsErrorEnum paramErrorEnum) {
super(paramErrorEnum.getMsg());
this.code = paramErrorEnum.getCode();
}
/**
* ParameterErrorException
*
* @param message message
*/
public ParameterException(String message) {
super(message);
this.code = CodeEnum.ErrorCodeEnum.ERROR_CODE_PARAMS_ERROR.getCode();
}
}
2、20000-業務異常
構造器 | 說明 |
BusinessException(CodeEnum.BusinessErrorEnum businessErrorEnum) | 建議使用:傳入一個固定的枚舉值 |
BusinessException(String message) | 不推薦:傳入一個錯誤資訊 |
@Getter
public class BusinessException extends RuntimeException {
/**
* serialVersionUID
*/
private static final long serialVersionUID = 799633539625676004L;
/**
* 傳回錯誤碼
*/
private final String code;
/**
* 展示資訊
*/
private final String showMsg;
/**
* BusinessException
*
* @param businessErrorEnum businessErrorEnum
*/
public BusinessException(CodeEnum.BusinessErrorEnum businessErrorEnum) {
super(businessErrorEnum.getMsg());
this.code = businessErrorEnum.getCode();
this.showMsg = businessErrorEnum.getShowMsg();
}
/**
* BusinessException
*
* @param message message
*/
public BusinessException(String message) {
super(message);
this.code = CodeEnum.ErrorCodeEnum.ERROR_CODE_BUSINESS_ERROR.getCode();
this.showMsg = message;
}
}
3、30000-RPC異常
構造器 | 說明 |
RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum) | 場景:處理未知的RPC異常,如網絡逾時等 |
RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum, String code, String showMsg) | 場景:處理已知的異常 |
RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum, String code, String msg, String showMsg) | 場景:處理已知的異常 |
@Getter
public class RpcException extends RuntimeException {
/**
* serialVersionUID
*/
private static final long serialVersionUID = 799633539625676004L;
/**
* 傳回錯誤碼
*/
private final String code;
/**
* 展示資訊
*/
private final String showMsg;
/**
* RpcException-處理未知的異常
*
*
* @param rpcErrorEnum rpcErrorEnum
*/
public RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum) {
super(rpcErrorEnum.getMsg());
this.code = rpcErrorEnum.getCode();
this.showMsg = CodeEnum.ErrorCodeEnum.ERROR_CODE_FAIL.getMsg();
}
/**
* RpcException 處理已知的異常
*
* @param rpcErrorEnum rpcErrorEnum
* @param code code
* @param showMsg showMsg
*/
public RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum, String code, String showMsg) {
super(rpcErrorEnum.getMsg());
this.code = rpcErrorEnum.getCode() + "_" + code;
this.showMsg = showMsg;
}
/**
* RpcException 處理已知的異常
*
* @param rpcErrorEnum rpcErrorEnum
* @param code code
* @param showMsg showMsg
*/
public RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum, String code, String msg, String showMsg) {
super(msg);
this.code = rpcErrorEnum.getCode() + "_" + code;
this.showMsg = showMsg;
}
}
4、40000-運作異常
@Getter
public class PlatformException extends RuntimeException {
private static final long serialVersionUID = 5535821215702463243L;
/**
* 傳回錯誤碼
*/
private final String code;
/**
* 展示資訊
*/
private final String showMsg;
/**
* PlatformException
*
* @param platformErrorEnum platformErrorEnum
*/
public PlatformException(CodeEnum.PlatformErrorEnum platformErrorEnum) {
super(platformErrorEnum.getMsg());
this.code = platformErrorEnum.getCode();
this.showMsg = CodeEnum.ErrorCodeEnum.ERROR_CODE_FAIL.getMsg();
}
}
四、異常吐出
1、10000-參數異常
/**
* 不支援的請求方始
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
@ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED)
public BaseRes<?> methodNotSupportExceptionHandler(HttpRequestMethodNotSupportedException e) {
log.error("不支援的請求方式", e);
return BaseRes.buildFailure(CodeEnum.ParamsErrorEnum.ERROR_CODE_10001.getCode(), e.getMessage());
}
/**
* 參數類型錯誤
*/
@ExceptionHandler(value = {BindException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> bindExceptionHandler(BindException e) {
log.error("====參數類型錯誤===", e);
return BaseRes.buildFailure(CodeEnum.ParamsErrorEnum.ERROR_CODE_10000);
}
/**
* 參數格式問題
*/
@ExceptionHandler(value = {MethodArgumentTypeMismatchException.class,
HttpMessageConversionException.class, UnexpectedTypeException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> httpMessageConversionExceptionHandler(Exception e) {
log.error("====參數格式異常===", e);
return BaseRes.buildFailure(CodeEnum.ParamsErrorEnum.ERROR_CODE_10002);
}
/**
* 參數錯誤
*/
@ExceptionHandler(value = ParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> parameterErrorExceptionHandler(ParameterException e) {
log.error("====參數異常:code:{},msg:{}", e.getCode(), e.getMessage(), e);
return BaseRes.buildFailure(e.getCode(), e.getMessage());
}
2、20000-業務異常
/**
* 業務異常,給前台傳回異常資料
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> businessExceptionHandler(BusinessException e) {
log.error("====業務異常:code:{},msg:{},showMsg:{}", e.getCode(), e.getMessage(), e.getShowMsg(), e);
return BaseRes.buildFailure(e.getCode(), e.getShowMsg());
}
3、30000-RPC異常
/**
* RPC,給前台傳回異常資料
*/
@ExceptionHandler(value = RpcException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> rpcExceptionHandler(RpcException e) {
log.error("====RPC異常:code:{},msg:{},showMsg:{}", e.getCode(), e.getMessage(), e.getShowMsg(), e);
return BaseRes.buildFailure(e.getCode(), e.getShowMsg());
}
4、40000-運作異常
/**
* 運作異常,給前台傳回異常資料
*/
@ExceptionHandler(value = PlatformException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> rpcExceptionHandler(PlatformException e) {
log.error("====運作異常:code:{},msg:{},showMsg:{}", e.getCode(), e.getMessage(), e.getShowMsg(), e);
return BaseRes.buildFailure(e.getCode(), e.getShowMsg());
}
五、Demo
1、10000-參數異常
@ApiOperation("parameterExceptionEnum")
@LogIndex
@GetMapping("parameterExceptionEnum")
@ResponseBody
public BaseRes<List<UserDemoVO>> parameterExceptionEnum() {
throw new ParameterException(CodeEnum.ParamsErrorEnum.ERROR_CODE_10002);
}
{
"success": false,
"data": null,
"msg": "參數格式異常",
"code": "10002"
}
@ApiOperation("parameterExceptionMsg")
@LogIndex
@GetMapping("parameterExceptionMsg")
@ResponseBody
public BaseRes<List<UserDemoVO>> parameterExceptionMsg() {
throw new ParameterException("使用者Id不能為空");
}
{
"success": false,
"data": null,
"msg": "使用者Id不能為空",
"code": "10000"
}
2、20000-業務異常
@ApiOperation("businessExceptionEnum")
@LogIndex
@GetMapping("businessExceptionEnum")
@ResponseBody
public BaseRes<List<UserDemoVO>> businessExceptionEnum() {
throw new BusinessException(CodeEnum.BusinessErrorEnum.ERROR_CODE_20001);
}
{
"success": false,
"data": null,
"msg": "您有一個訂單正在建立,請稍後檢視",
"code": "20001"
}
@ApiOperation("businessExceptionMsg")
@LogIndex
@GetMapping("businessExceptionMsg")
@ResponseBody
public BaseRes<List<UserDemoVO>> businessExceptionMsg() {
throw new BusinessException("使用者建立失敗");
}
{
"success": false,
"data": null,
"msg": "使用者建立失敗",
"code": "20000"
}
3、30000-RPC異常
@ApiOperation("rpcExceptionDefaultEnum")
@LogIndex
@GetMapping("rpcExceptionDefaultEnum")
@ResponseBody
public BaseRes<List<UserDemoVO>> rpcExceptionDefaultEnum() {
throw new RpcException(CodeEnum.RpcErrorEnum.ERROR_CODE_USER_0_30001_0001);
}
{
"success": false,
"data": null,
"msg": "系統太火爆了,請稍後重試!",
"code": "USER_0_30001_0001"
}
@ApiOperation("rpcExceptionEnumShowMsg")
@LogIndex
@GetMapping("rpcExceptionEnumShowMsg")
@ResponseBody
public BaseRes<List<UserDemoVO>> rpcExceptionEnumShowMsg() {
throw new RpcException(CodeEnum.RpcErrorEnum.ERROR_CODE_USER_1_30001_0001, "1000", "使用者不存在");
}
{
"success": false,
"data": null,
"msg": "使用者不存在",
"code": "USER_1_30001_0001_1000"
}
@ApiOperation("rpcExceptionEnumMsg")
@LogIndex
@GetMapping("rpcExceptionEnumMsg")
@ResponseBody
public BaseRes<List<UserDemoVO>> rpcExceptionEnumMsg() {
throw new RpcException(CodeEnum.RpcErrorEnum.ERROR_CODE_USER_1_30001_0001,
"1000", "底層結構異常", "使用者不存在");
}
{
"success": false,
"data": null,
"msg": "使用者不存在",
"code": "USER_1_30001_0001_1000"
}
4、40000-運作異常
@ApiOperation("platformException")
@LogIndex
@GetMapping("platformException")
@ResponseBody
public BaseRes<List<UserDemoVO>> platformException() {
throw new PlatformException(CodeEnum.PlatformErrorEnum.ERROR_CODE_40001);
}
{
"success": false,
"data": null,
"msg": "系統太火爆了,請稍後重試!",
"code": "40001"
}
作者:京東保險 張宇晉
來源:京東雲開發者社群 轉載請注明來源