关于错误码
初学编程,在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分钟了怎么办,再加个系统参数来控制?如果后续这个参数要改动,谁来及时通知我们,这样是不是太耦合了?后续再接入其他重试时长不一样的客户系统呢?
当然这个例子是特例,比较极端,也许一般直接告诉客户不能重试就拉倒了。。。
相关文章:

关于错误码
初学编程,在C语言中定义错误码,是使用宏: #define SUCCESS 0 //成功 #define FAILED 1 //失败 后来知道,用枚举更适合,因为宏的名声实在不好,而枚举可以帮你自动编号,减少…...

Linux | gcc/g++的使用
目录 前言 一、程序的翻译过程 1、预编译 2、编译 3、汇编 4、链接 (1)链接做了什么 (2)动态链接 (3)静态链接 (4)如何使用gcc进行动态链接和静态链接 前言 本章主要带着大…...

了解容器运行时安全:保护你的容器应用
前言 容器是一种虚拟化技术,用于封装和运行应用程序及其依赖项,以便在不同的计算环境中保持一致性和可移植性。自2013年容器诞生至今,容器Docker镜像的下载量超20亿,虽然容器行业发展如火如荼,但是其安全风险却不容乐…...

大规模语言LLaVA:多模态GPT-4智能助手,融合语言与视觉,满足用户复杂需求
大规模语言LLaVA:多模态GPT-4智能助手,融合语言与视觉,满足用户复杂需求 一个面向多模式GPT-4级别能力构建的助手。它结合了自然语言处理和计算机视觉,为用户提供了强大的多模式交互和理解。LLaVA旨在更深入地理解和处理语言和视…...

Element UI定义方法校验邮箱格式
1. .vue中定义了3个邮箱输入框,前面两个是输入多个邮件地址,最后一个是使用element自带的校验来校验单个邮件地址 <el-row><el-col :span"24"><el-form-item label"收件人" prop"to"><el-input v-mo…...

DigiCert代码签名证书
在当今的数字化时代,确保软件的完整性和来源的可靠性至关重要。DigiCert代码签名证书是一种专业而受信任的数字签名解决方案,帮助开发者保护其软件免受篡改和恶意行为的侵害。本文将介绍DigiCert代码签名证书的特点和优势,并讨论为何选择Digi…...

HTML5+CSS3小实例:网页底部间隔波浪动画特效
实例:网页底部间隔波浪动画特效 技术栈:HTML+CSS 效果: 源码: 【html】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content…...

前端 js 之 代码执行的一个过程 02
嘿,欢迎你来 !💕 文章目录 前言一、运行一个 js 文件二、运行环境三、js执行代码的过程(普通变量)四、打印 window五、js执行代码的过程(函数变量)六、函数调用函数的过程注意!&…...

【经验分享】如何构建openGauss开发编译提交一体化环境
前文 本文适合对openGauss源代码有好奇心的爱好者,那么一个友好的openGauss源代码环境应该是怎么样的。openGauss的开发环境是如何设置的?openGauss的编译环境是如何构建的?如何向openGauss提交代码,笔者集合官方和几位博主实践提…...

儿童疫苗接种:安全与注意事项
引言: 儿童的疫苗接种是维护其健康和预防传染病的重要措施。疫苗可以有效地保护儿童免受各种疾病的威胁,但在接种过程中需要家长和监护人特别关注一些注意事项,以确保接种的安全性和有效性。本文将深入探讨儿童疫苗接种的重要性,…...

Go 代码块与作用域,变量遮蔽问题详解
Go 代码块与作用域详解 文章目录 Go 代码块与作用域详解一、引入二、代码块 (Block)2.1 代码块介绍2.2 显式代码块2.3 隐式代码块2.4 空代码块2.5 支持嵌套代码块 三、作用域 (Scope)3.1 作用域介绍3.2 作用域划定原则3.3 标识符的作用域范围3.3.1 预定义标识符作用域3.3.2 包代…...

可观测性-Metrics-WebClient异步Http远程Call
代码示例 1.依赖导入 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><ar…...

Android之播放本地视频和Url视频方法
一、播放本地视频文件 根据文件路径在浏览器中播放,可用于视频预览等场景 效果: 用浏览器播放本地视频 文件路径例子: /storage/emulated/0/Android/data/com.custom.jfrb/files/Movies/1697687179497.mp4 File file new File("文件…...

设计模式:工厂方法模式(C#、JAVA、JavaScript、C++、Python、Go、PHP):
本节主要介绍设计模式中的工厂方法模式。 简介: 工厂方法模式,它是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。 它定义了一个用于创建对象的工厂接口,让…...

C++基础——指针
1 概述 指针的作用:可以通过指针间接访问内存 内存编号从0开始,一般使用十六进制数字表示,指针可以保存地址 2 指针变量定义和作用 int main() {//1、指针的定义int a 10; //定义整型变量a//指针定义语法: 数据类型 * 变量名 …...

PLC 学习day02 硬件输入/输入的知识
1.资料来源 1.链接:三菱PLC视频教程全集之FX3U基本单元输入接线_哔哩哔哩_bilibili 2. 链接: 三菱plc视频教程全集之FX3U基本单元输出接线_哔哩哔哩_bilibili 2. PLC 的输入部分器件连接。 2.1 PLC输入部分的硬件知识 1. 一般输入部分是PLC获取信息的地…...

rabbitMq (2)
RabbitMQ 消息应答与发布 文章目录 1. 消息应答1.2 自动应答1.2 手动应答1.3 代码案例 2. RabbitMQ 持久化2.1 队列持久化2.2 消息持久化 3. 不公平分发4. 预取值分发5. 发布确认5.1 发布确认逻辑5.2 开启发布确认的方法5.3 单个确认发布5.4 批量确认发布5.5 异步确认5.5.1 处理…...

通讯协议学习之路:RS422协议理论
通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN;视频会发布在bilibili(UID:399951374) 一、…...

剪映failed to initialize,cuda.is_available()为false解决
debug记录帖 错误1:打开剪映发现弹窗提示failed to initialize graphics backed for D3D11 错误2:torch版本、cuda版本(之前的正常环境)都对但是torch.cuda.is_available()为false 怀疑是显卡驱动的问题 打开Nvidia Geforce Exp…...

基于Spring Boot的LDAP开发全教程
写在前面 协议概述 LDAP(轻量级目录访问协议,Lightweight Directory Access Protocol)是一种用于访问和维护分布式目录服务的开放标准协议,是一种基于TCP/IP协议的客户端-服务器协议,用于访问和管理分布式目录服务,如企业内部的…...

在 Linux 上保护 SSH 服务器连接的 8 种方法
SSH 是一种广泛使用的协议,用于安全地访问 Linux 服务器。大多数用户使用默认设置的 SSH 连接来连接到远程服务器。但是,不安全的默认配置也会带来各种安全风险。 具有开放 SSH 访问权限的服务器的 root 帐户可能存在风险。尤其是如果使用的是公共 IP 地…...

摩尔信使MThings的协议转换(数据网关)功能
摩尔信使MThings可以作为现场总线(RS485)和以太网的数据中枢,并拥有强大的Modbus协议转换功能。 数据网关功能提供协议转换和数据汇聚功能,可实现多维度映射,包括:不同的通道(总线)类型、协议类型ÿ…...

Mac安装Kali保姆级教程
Mac安装Kali保姆级教程 其他安装教程:使用VMware安装系统Window、Linux(kali)、Mac操作系统 1 虚拟机安装VM Fusion 去官网下载VM Fusion 地址:https://customerconnect.vmware.com/en/evalcenter?pfusion-player-personal-13 …...

利用Spring Boot框架做事件发布和监听
一、编写事件 1.编写事件类并集成spring boot 事件接口,提供访问事件参数属性 public class PeriodicityRuleChangeEvent extends ApplicationEvent {private final JwpDeployWorkOrderRuleDTO jwpDeployWorkOrderRuleDTO;public PeriodicityRuleChangeEvent(Obje…...

KingBase库模式表空间和客户端认证(kylin)
库、模式、表空间 数据库 数据库基集簇与数据库实例 KES集簇是由单个KES实例管理的数据库的集合KES集簇中的库使用相同的全局配置文件和监听端口、共享相关的进程和内存结构同一数据库集簇中的进程、相关的内存结构统称为实例 数据库 数据库是一个长期存储在计算机内的、有…...

h5的扫一扫功能 (非微信浏览器环境下)
必须在 https 域名下才生效 <template><div><van-field label"服务商编码" right-icon"scan" placeholder"扫描二维码获取" click-right-icon"getCameras" /> <div class"scan" :style"{disp…...

Typora 导出PDF 报错 failed to export as pdf. undefined 解决方案
情况 我想把一个很大的markdown 导出为 248页的pdf 然后就报错 failed to export as pdf. undefined 原因 : 个人感觉应该是图片太大了 格式问题之类导致的 解决 文件 -> 偏好设置 - > 导出 -> pdf -> 自定义 -> 把大小全部改为24mm (虽然图中是32 …...

[架构之路-239]:目标系统 - 纵向分层 - 中间件middleware
目录 前言: 一、中间件概述 1.1 中间件在软件层次中的位置 1.2 什么是中间件 1.3 为什么需要中间件 1.4 中间件应用场合(应用程序不用的底层需求:计算、存储、通信) 1.5 中间件分类 - 按内容分 二、嵌入式系统的中间件 2…...

javascript利用xhr对象实现http流的comet轮循,主要是利用readyState等于3的特点
//此文件 为前端获取http流 <!DOCTYPE html> <html xmlns"http://www.w3.org/1999/xhtml" lang"UTF-8"></html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"/&g…...

【Mybatis源码】XPathParser解析器
XPathParser是Mybatis中定义的进行解析XML文件的类,此类用于读取XML文件中的节点文本与属性;本篇我们主要介绍XPathParser解析XML的原理。 一、XPathParser构造方法 这里我们介绍主要的构造方法 public XPathParser(InputStream inputStream, boolean validation, Propert…...