首先,要明确一点的是,错误码和异常,这两者在程序的表达能力上是等价的。

它们都可以向调用者传达“与常规情况不一样的状态”。

考虑使用哪一种,更多地是从API的设计、系统的性能指标、新旧代码的一致性这3个角度来考虑的(本文主要从API设计的角度着手)。

我们返回异常, 比如return ResultUtil.fail( errorCode) , 如果直接把异常返回到用户使用层面 , 会有下面两点问题

  1. 总不能指望用户看懂java 的Exception
  2. 直接返回异常信息, 可能会造成安全问题

多使用异常机制的优点

  1. 不需要校验各种状态
  2. 而且异常信息方便排查错误
    • 状态码字段
    • 描述信息
    • 异常调用栈
  3. 可以多自定义业务异常 , 然后使用异常机制,而不是使用状态码,然后各种校验。

举个例子, 注意下面的代码

1
2
3
4
5
6
7
8
9
10
if(StringUtils.isAnyBlank(userAccount, userPassword,checkPassword)){
return Result.fail("账户或密码不能为空!");
}
if(!userPassword.equals(checkPassword)){
return Result.fail("两次输入的密码不一致!");
}
if(RegexUtils.isStuCodeInvalid(stuCode))
{
return Result.fail("请检查输入的学号是否正确!");
}

对于上面的每一种出现错误, 我们都需要手动去返回fail, 这种方式与

throw new BusinessException(ErrorCode.PARAMS_ERROR,"账户或密码不能为空!");

看起来似乎一致, 返回的信息也大差不差

但是前提是当前我们实现的功能尚且简单, 逻辑比较简单, 并且出现异常的情况也比较少

那么如果我们以后业务复杂, 代码繁多, 如果还像上面这样一个一个的去手动返回异常,

效率将会十分低下, 而且代码也会看起来是否的臃肿

在这里的解决方法是定义异常类, 然后设置全局异常处理器 , 统一捕获异常,

然后以Result的形式返回

比如上面的代码就改为

1
2
3
4
5
6
7
8
9
10
if(StringUtils.isAnyBlank(userAccount, userPassword,checkPassword)){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"账户或密码不能为空!");
}
if(!userPassword.equals(checkPassword)){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"两次输入的密码不一致!");
}
if(RegexUtils.isStuCodeInvalid(stuCode))
{
throw new BusinessException(ErrorCode.PARAMS_ERROR,"请检查输入的学号是正确!");
}

规则1:本次发生的异常现象是不是真的非常罕见?非常罕见意味着发生该现象的机率非常低,调用方每次都处理将浪费大量的精力,但如果出现,系统应该明确终止,而不是继续往下执行,所以使用异常;其它情况,参考规则2。

规则2:本次发生的异常现象是不是出乎意料之外?出乎意料之外意味着系统已经出问题了(状态和预期不一致),被调用方没办法再往下执行了,而调用方如果不明确处理的话,系统也应该终止(因为系统状态出问题了),所以使用异常;其它情况,参考规则3。

规则3:遇到模凌两可的情况,则根据系统的性能需求(异常使用不当可能会造成系统抖动)、现有代码的情况(要保持一致的程序风格)以及开发人员的口味(怎么舒服怎么来)。

阿里规范

3.在代码中使用“抛异常”还是“返回错误码”?

  • 对于公司外的 http/api 开放接口必须 使用“错误码”;而应用内部推荐异常抛出;跨应用间 RPC 调用优先考虑使用 Result 方式,封 装 isSuccess(通信状态)、“错误码”(业务状态)、“错误简短信息”。