网站建设先航科技,wordpress主题巴士,土石方工程网站,网站内容改版初学编程#xff0c;在C语言中定义错误码#xff0c;是使用宏#xff1a;
#define SUCCESS 0 //成功
#define FAILED 1 //失败
后来知道#xff0c;用枚举更适合#xff0c;因为宏的名声实在不好#xff0c;而枚举可以帮你自动编号#xff0c;减少…初学编程在C语言中定义错误码是使用宏
#define SUCCESS 0 //成功
#define FAILED 1 //失败
后来知道用枚举更适合因为宏的名声实在不好而枚举可以帮你自动编号减少错误码冲突还有编译期校验有调试器支持。
enum errorcodes {ERR_SUCCESS 0, //成功ERR_FAILED 1 //失败
}
不过C语言枚举比较烦的一个地方就是要求不同枚举类型里的枚举名字必须不一样。所以不得不在每个枚举名前面加个前缀比如上面的ERR_来防止和其他枚举类型里面的枚举名字冲突。
还有就是最后一项枚举名后面不能有逗号这也使维护枚举列表很麻烦。不过后来的C标准好像是C99进行了人性化改进允许最末尾一项后面存在逗号。
C的枚举本质上还是整数而Java的枚举更加灵活可以为每个枚举项定义对应的数值和文本以及其他数据类型
enum ErrorCode
{SUCCESS(0, 成功),FAILED(1,失败),;private final int code;private final String msg;ErrorCode(int code, String msg) {this.code code;this.msg msg;}public int getCode() { return code; } public String getMsg() { return msg; }
}
不过上面的msg主要还是起到一个注释的效果通常会在资源文件中定义错误码对应的文本以支持多语言和动态提示。
实际项目中不会让每个程序员按各自偏好的风格来定义各自的错误码而是对错误码进行全局统一管理比如所有代码共享一个公共的ErrorCode类进行分段并在开头添加注释说明
/* 错误码划分注意遵守
0 成功
00001-10000 公共错误
10001-20000 数据库错误
20001-30000 外部系统错误
30001-40000 模块自定义错误30001-31000 A模块或A特性31001-32000 B模块或B特性
*/
有的还会事先定义一些边界错误码来占位
COMMON_START 1, //公共错误码开始//公共错误码在这里加
COMMON_END 10000, //公共错误码结束DB_START_ 10001, //数据库错误码开始//数据库错误码在这里加
DB_END 20000, //数据库错误码结束
即使如此不同的程序员在错误码的设计粒度上还会有分歧。比如 有人只用一个“参数错误”来表示所有的接口入参问题的错误码 有人认为应该区分“必填参数为空”和“参数错误” 有人甚至给每个参数错误都设计不同的错误码分别对应“A参数为空”“A参数超长”“A参数太短”“B参数为空”“B参数超长”等等。 一般还是认为“过犹不及”差不多能让客户端明确错误原因就行。比如“参数错误”可以用同一个错误码但加上动态的错误提示信息来表明具体是哪个参数错了错在哪里。
但如果错误码定义太宽泛一个错误码对应太多的失败场景导致开发者在收到客户报错信息时自己也是一头雾水只能查看系统运行日志才能确定问题所在那表示错误码可能需要细化了。见到过一些极端的做法是在错误码或者错误提示内加入文件名和行号用于直接找到源代码出错点而不用查看系统日志
微软甚至有个api指导文档支持N层的错误码其中错误码是字符串类型不是整型https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses
{error: {code: BadArgument,message: Previous passwords may not be reused,target: password,innererror: {code: PasswordError,innererror: {code: PasswordDoesNotMeetPolicy,minLength: 6,maxLength: 64,characterTypes: [lowerCase,upperCase,number,symbol],minDistinctCharacterTypes: 2,innererror: {code: PasswordReuseNotAllowed}}}}
}
曾经做过一个比较复杂的规则查询接口为了让调用者知道具体是什么原因导致业务被禁止给出了三段子错误码比如 子错误提示根据[付款方式]判断您的订单[用优惠券]因此不支持[退款]。 子错误码1-3-1 其中1付款方式3用优惠券1退款分别是三个不同维度下的枚举值。根据业务规则存在成百上千种组合关系。如果客户想要获得全部错误码的文档我们会遗憾地告诉他“给不了是动态生成的没法穷尽”。其实枚举项和它们对应的规则组合总数还是有限的只不过枚举偶尔变业务规则总在变。
如果客户想要自己定制文案提供给客户的客户最不灵活而我们比较省事的做法是我们把这些枚举值定义的表格都从数据库导出来让客户自己去根据错误码生成文案。但以后我们每次增加枚举都得通知客户。第二种方式是另外专门提供一个接口供客户随时查询枚举值表或规则表剩下的客户自己解决这里也还要考虑变更如何让客户端知道比如客户端每隔多久轮询刷新一次就可以了不要求太实时或者麻烦点让客户给个反向通知接口给我们调。第三种是我们全包了为不同客户定制不同的提示文案根据不同的客户id进行返回。文案模板如果放我们这里又有耦合如果让客户每次请求都携带会浪费带宽。或者让客户只带模板id不带文案额外给我们提供“根据id查询文案内容”的接口这又太麻烦了。。。 错误码的文档化有时还会遇到其他麻烦特别是遇到严格的外部客户。
比如客户要求确切地给出每个接口能返回哪些错误码。虽然有swagger等工具但好像也只能把所有全局错误码都堆上去而不能判断代码的执行路径自动提取对应的错误码并生成文档。如果之前写代码时没有整理过错误码这时恐怕就只能人工走一遍从接口进入的所有代码分支路径找到对应的错误码。如果接口多搞一次非常耗时。或者是改代码来简化错误码在接口层对其他层的错误码做一次映射比如把所有数据库错误或其他系统的500都映射成为一个“系统内部错误”再返回给客户端。不过这样做是否合适对此大家的意见可能会有分歧。
或者客户要求在文档中指出哪些错误码是可以重试的因为它不是用户触发的请求要靠系统发起重试。以HTTP的状态码为例4xx是客户端错误怎么重试都应该会得到相同的错误所以不建议重试而5xx是服务端错误重试是有可能成功的。但像我们之前定义的全局错误码不是按这个维度分段而是按照模块或功能分段的那就只能逐个错误码去挑出来并进行标识说明。这也是费时的工作。要保证准确还得检查错误码的各个使用点是否合理而且后续新增和使用错误码都要考虑这个【是否支持重试】的维度。
另外如果硬要死抠“是否可重试”也会比较纠结。
比如客户说他们的重试时间是30分钟而我们提供的某个接口业务规则是“接口调用时间要在下单时间的1小时之后且在24小时内”对应某一个错误码。实际上每个订单的下单时间都不同如果收到请求时这个订单才下单45分钟那么它重试后应该能成功。如果已经下单30小时了那怎么重试都不行。
如果文档简单告诉客户遇到这个错误码不能重试会导致丢失不少业务带来不少客诉。如果告诉客户全都能重试他们可能又会投诉“每天很多重试都失败了影响了我们的系统监控解决一下”
如果客户愿意配合改造我们可以在响应消息中加上额外的时间信息来帮助他们判断是否能够重试但这种客户可能不多因为太麻烦了还会认为增加了与我们业务逻辑的耦合。如果客户一定要求我们来区分两种错误码30分钟后重试能成功30分钟后重试能成功我们也会很尴尬万一后续客户重试时间不是30分钟了怎么办再加个系统参数来控制如果后续这个参数要改动谁来及时通知我们这样是不是太耦合了后续再接入其他重试时长不一样的客户系统呢
当然这个例子是特例比较极端也许一般直接告诉客户不能重试就拉倒了。。。