让错误码规范起来吧

京东云开发者
• 阅读 209

一、前言

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"
}

作者:京东保险 张宇晋

来源:京东云开发者社区 转载请注明来源

点赞
收藏
评论区
推荐文章
一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器 | 京东云技术团队
GracefulResponse是一个SpringBoot体系下的优雅响应处理器,提供一站式统一返回值封装、异常处理、异常错误码等功能。使用GracefulResponse进行web接口开发不仅可以节省大量的时间,还可以提高代码质量,使代码逻辑更清晰。
Wesley13 Wesley13
3年前
HTTP,FTP,SMTP错误码
HTTP100Continue继续101SwitchingProtocols切换协议200OK正常201Created已创建202Accepted已接受203NonAuthoritativeInformation非权威性信息204NoContent无内容
Stella981 Stella981
3年前
Nginx HTTP 413 Request Entity Too Large
nginx做代理上传文件时返回HTTP413错误码,提示信息如下:413RequestEntityTooLargeTherequestedresourcedoesnotallowrequestdatawiththerequestedmethodortheamountofdataprovide
Stella981 Stella981
3年前
Linux标准错误码(全)
Errors:LinuxSystemErrorsWhensystemrequestsfail,errorcodearereturned.Tounderstandthenatureoftheerrorthesecodesneedtobeinterpreted.Theyarerecorded
Wesley13 Wesley13
3年前
oracle存储过程迁移达梦心得
这几天把项目的存储过程从oracle迁移到了达梦8,记录一下心得。国产数据库做到这样,已经算很了不起了,跟oracle兼容性确实很高。但还是有一些细节没做好,主要是出错提示不友好,另外一个网上的资料也很少,出问题不好定位。(达梦的错误码比较简单,就是一个负数,不像oracle是ora错误码,在百度时输入一个负数搜索信息,跟一长串错误码搜索效率差很远)
融云IM即时通讯 融云IM即时通讯
18小时前
融云IM干货丨IM服务,在开发过程中,如何自动化处理SDK日志中的错误码
在开发过程中,自动化处理SDK日志中的错误码可以通过以下几个步骤实现:错误码解析:利用SDK提供的错误码对照表,将错误码映射到具体的错误信息和解决方案。例如,阿里云日志服务提供了详细的错误码对照表及对应的解决方法。异常捕获与处理:SDK通常会抛出异常来处理
小万哥 小万哥
11个月前
全面的开发者文档和用户目标解析:API 文档指南和开发者旅程
开发者文档开发者文档,也称为API文档,是一种专门针对软件开发人员的技术写作形式。这种类型的文档通常包括API的技术规范、代码注释、软件设计和架构以及软件开发中涉及的其他详细技术描述。开发者文档是开发人员的重要工具,因为它提供了使用和集成特定软件、库或AP
京东云开发者 京东云开发者
5个月前
一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器
1.前言统一返回值封装、统一异常处理和异常错误码体系的意义在于提高代码的可维护性和可读性,使得代码更加健壮和稳定。统一返回值封装可以避免每一个接口都需要手工拼装响应报文;统一异常处理可以将异常处理的逻辑集中到一个地方,避免代码中出现大量的trycatch语
融云IM即时通讯 融云IM即时通讯
18小时前
融云IM干货丨IM服务消息推送,推送通知失败时,SDK日志能提供哪些额外信息?
推送通知失败时,SDK日志可以提供以下额外信息帮助诊断问题:错误码和错误描述:SDK可能会返回具体的错误码和对应的错误描述,这些信息可以帮助开发者快速定位问题。例如,腾讯云推送服务的错误码列表中包含了各种错误码及其含义,如1008006表示Token无效,
融云IM即时通讯 融云IM即时通讯
18小时前
融云IM干货丨IM服务消息推送,如何解读SDK日志中的错误码?
解读SDK日志中的错误码通常涉及以下几个步骤:识别错误代码(ErrorCode):错误代码是标识错误类型的数字或字符串。它通常与API返回的错误代码一致,如果是SDK网络请求错误,则错误代码可能是特定的,比如“RequestError”。查看错误消息(Er