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。