API的错误处理
正常结果 Response
当我们在定义一个函数接口的时候,往往会定义:
- 接口名
- 输入参数 Reqest
- 返回结果 Response
以java为例,登陆接口定义会是类似:
User login(String username, String password);
异常结果 BizError
上述的接口定义,其实是不妥的,因为它定义了所谓的Happy Path 主流程需要的正常结果 Response;却没有定义Sad Path 分支流程所需要的异常结果 Biz Error。
需要注意的是:主流程与分支流程都属于业务逻辑的一部分,给与分支流程足够的重视,是软件成熟度提升的表现。
登陆是完全可能出现分支流程的,比方说,用户名/密码错误,账号被屏蔽等等;一个连密码错误处理不了的登陆程序,是非常糟糕的程序。
所以,API的定义可以调整为:
User login(String username, String pass) throw InvalidLoginException, AccountBannedException;
当然,我们也可以把InvalidLoginException跟AccountBannedException都归为LoginError,然后使用Enum枚举属性来区分,那么接口会变成:
enum ErrorTypes {
InvalidLogin = 1;
AccountBanned = 2;
}
Class LoginError {
ErrorTypes Error;
}
User login(String username, String pass) throw LoginError;
常见异常 CommonError
但这依旧不够,我们实现接口的时候,往往会使用一些框架、中间件,而它们自身又经常带有一些内置的常见异常 CommonError,比方说:
- APIKeyNotProvided
- InvalidParameter
等等,同样的,我们也可以把上述归类为CommonError,那么API定义会变成:
enum ErrorTypes {
InvalidLogin = 1;
AccountBanned = 2;
}
Class LoginException {
ErrorTypes Error;
}
Class FrameworkException {
....
}
User login(String username, String password) throw LoginError, CommonError;
CommonError与BizError的区别在于后者是针对单一API的,而前者则可能会存在于大部分,甚至所有API。
BizError是由对当前的API提供方的业务代码返回,需要调用方针对返回的BizError进行处理,然后进入当前业务的分支流程,这样的流程是需要我们根据业务进行特定编码;它是与业务逻辑代码强相关的。
而CommonError被返回时,则可能是跟具体业务无关,它一般是由API提供方使用的框架、中间件直接返回的,调用方收到CommonError时,可能不会进入业务的分支流程,而是进行一些同样的处理,比方说,提示用户登录后操作,或者重新输入。
它需要被底层绑定的通用代码,而不是当前的业务代码处理。
错误 Error
程序运行的时候,是可能遇到比异常更加严重的Error,比方说:TimeOutError、OutOfMemeryError,甚至SegmentFaultError等等。
API遇到错误的时候,无论是提供方还是调用方,几乎都无法进行任何具体处理,最多由通用代码打一下日志,然后提示一下用户稍后重试。
我们只有把:
- 正常结果
- 异常结果
- 常见异常
- 错误
一个成熟的API,需要对这四种四者都考虑进去。
总结
| 正常结果 | 异常结果 | 常见异常 | 错误 | |
|---|---|---|---|---|
| 业务 | 强相关 | 强相关 | 弱相关 | 无关 |
| 返回者 | 业务代码 | 业务代码 | 框架/中间层 | 其它 |
| 处理者 | 业务代码 | 业务代码 | 通用代码 | 通用代码 |
| Response | BizError | CommonError | Error | |
|---|---|---|---|---|
| Business Relation | Strong | Strong | Weak | N.A. |
| Returner | Biz Code | Biz Code | Framework/Middleware | Others |
| Handler | Biz Code | Biz Code | General Code | General Code |
下一节,我会推荐一种通用的API框架/风格 - protoapi,鼓励按正常结果、异常结果、常见异常、错误这四种划分来定义我们的API。