简介

  • 服务器接口设计中最重要的环节之一便是接口错误码的定义了,通常情况下服务端会定义一些列错误码用以指示接口调用者或者用户进行正确的操作。例如接口参数确实、参数非法、无权限访问、用户身份认证信息过期等等类似反馈。

** 本文将以错误码的类型、易用性、易读性、简洁性等方面进行讲解,内容有**

  • 接口错误码是用来干嘛的?
  • 接口错误码有哪些类型?
  • 接口错误码的定义方式有哪些?
  • 设计的接口错误码需要具有哪些能力?
  • 设计接口错误码的最佳实践

原文:地址

1. 接口错误码是用来干嘛的?

接口错误码,顾名思义,肯定是调用接口失败后反馈给客户端的信息,一般我们调用第三方的开放接口会看到一些非常规范的错误码定义,如下:

  • 系统错误示例:
{"code": 0x02001023,
"msg": "应用秘钥错误"
}
{"code": 0x02001111,
"msg": "系统未知错误,请稍后重试!"
}
  • 复杂错误类型示例:
{"code": 0x02001024,
"msg": "请求参数非法",
"data": {  "subErrors": [  {    "error": "NotEmpty",   "msg": "请求参数name为必填项"   },    {    "error": "NotNull",    "msg": "请求参数age为必填项"    }    ]    }
}
  • 其他错误响应示例
{"code": 0x0210ffc1,
"subCode": "LoginAccountNotFound",
"msg": "未找到该账户信息,请核实后再登录!"
}

通过上面示例的错误码得知,错误码的作用有

  1. 诱导接口调用者使用正确的调用方式
  2. 指示调用方依据不同的错误码做逻辑控制处理
  3. 指示用户,引导用户进行正确的操作
  4. 明确指示服务器接口处理异常信息,便于开发人员及时发现与排查

所以设计一个好用又方便的错误码体系很重要!

2. 接口错误码有哪些类型?

主要分为两大类,系统错误码和业务错误码

  • 系统错误码: 一些通用错误信息的定义,一般用于指示开发者正确的进行接口调用和告知调用者接口服务的状态信息。
  • 业务错误码:根据具体业务流程提示或诱导用户进行正确的操作,如用户登录时,账号密码输入错误,接口错误码和提示信息会引导用户重新检查账号密码的正确性并进行重试。

3. 接口错误码的定义方式有哪些?

我们做开发时经常会使用到别人提供的接口,比如百度开放平台、支付宝、微信公众号提供的开放API等等。他们都会提供一些系统错误码说明,常见的形式有:

  1. 一个数字型的code标识

    • 长度可控,且字段值比较短,可节省传输带宽
    • 可读性不高,看到错误码后需要参考文档才能知其含义
    • 一般都会通过数字的范围对code进行分段,用于标识不同子服务、业务等等
  2. 一个字符串code标识

    • 长度会比数值型code长
  • 可读性高,见错误码便知其意
  • 通过业务领域对应的名字来进行描述,例如: 服务名+操作类型+失败原因

4. 我设计的接口错误码需要具有哪些能力?

本文所设计的错误码需要兼顾以下几个特点

  • 可读性一定要高
  • 兼容性要好,要能支持常见的两种错误码类型,字符串和数值型都需要提供
  • 易于维护,错误码要便于维护
  • 易用性,错误码的定义要对开发人员友好,最好开发人员不需要关心错误码的值
  • 灵活可控,错误码可以手动指定,也可以自动进行维护,而且支持错误码自动分段和手动分段

5. 接口错误码的最佳实践

5. 1 错误码的格式和模式的选择
  • 错误码格式采用如下方式
{"code": 0x0210ffc1,
"strCode": "LoginAccountNotFound",
"msg": "未找到该账户信息,请核实后再登录!",
"data":{}
}
  • code: Long型错误码
  • strCode: 字符串类型错误码
  • msg: 描述信息
  • data: 正常响应内容,json对象,由具体业务接口进行灵活指定
  • 正常响应结构:
{"code": 0,
"data":{}
}
  • 错误响应结构(兼容模式):
{"code": 0x0210ffc1,
"strCode": "LoginAccountNotFound",
"msg": "未找到该账户信息,请核实后再登录!"
}
  • 错误响应结构(数值型模式):
{"code": 0x0210ffc1,
"msg": "未找到该账户信息,请核实后再登录!"
}
  • 错误响应结构(字符串型模式):
{"code": "LoginAccountNotFound",
"msg": "未找到该账户信息,请核实后再登录!"
}
5. 2 错误码的定义
  • 系统错误码的定义
 @Getter@AllArgsConstructorpublic enum ErrorCodes {SUCCESS(0,"success"),//成功SYSTEM_PARAM_LOST(0x06001000,"接口参数缺失"),//... 其他错误码的定义;    private Long code;private String msg;}
  • 业务错误码的定义
 @Getter@AllArgsConstructorpublic enum BussinessError {   TEST_EXAMPLE_OLD_ERROR(0x06600001, "test.exampleOldError", "完整错误码示例")//手动指定错误码  ,LOGIN_ACCOUNT_NOT_EXISTS("login.accountNotExists", "用户登录账号【%s】不存在")//不指定数值型错误码,将会自动生成//... 其他错误码的定义;private Integer code;private String strCode;private String msg;private BussinessError(int code,String strCode){this.code=code;this.strCode=strCode;       }private BussinessError(String strCode, String msg){this.strCode=strCode;this.msg=msg;}private BussinessError(String strCode){this.strCode=strCode;}/*** 获取参数化的msg值*/public String getMsgParams(Object ...params){if(this.msg!=null){return String.format(this.msg, params);}return this.msg;}}
5. 3 业务错误码的自动生成
  • 通过错误码枚举成员字段名的字符串生成唯一标识数值,能有效保证字符串错误码不重复
/**  * Title HashUtil.java  * Description  * @author danyuan* @date Nov 8, 2019* @version 1.0.0* site: www.danyuanblog.com
*/
package com.danyuanblog.common.util;public class HashUtil {public static int ELFhash(String str)//思想就是一直杂糅,使字符之间互相影响{int h = 0x0, g;for(int i = 0 ; i < str.length() ; i++){h = (h<<4) + str.charAt(i); //h左移4位,当前字符占8位,加到h中进行杂糅if((g = h & 0xf0000000) != 0) //取h最左四位的值,若均为0,则括号中执行与否没区别,故不执行{h ^= g>>24; //用h的最左四位的值对h的右起5~8进行杂糅h &= ~g;//清空h的最左四位}}return h; //因为每次都清空了最左四位,最后结果最多也就是28位二进制整数,不会超int}public static int limitELFHash(String str , int min, int max){int k = ELFhash(str);k = Math.abs(k - min);int result = k % (max - min);  result += min;        return result;}
}
  • 指定范围的错误码生成
/**  * Title ErrorCodeUtil.java  * Description  错误码工具包* @author danyuan* @date Nov 8, 2019* @version 1.0.0* site: www.danyuanblog.com
*/
package com.danyuanblog.common.util;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import lombok.extern.slf4j.Slf4j;import com.danyuanblog.base.exception.ErrorCodes;
import com.danyuanblog.common.consts.BussinessErrors;
import com.danyuanblog.common.exception.ErrorCodeDto;@Slf4j
public final class ErrorCodeUtil {private final static HashMap<String, Integer> codeMap = new HashMap<>();//定义错误码范围为: 0x02100000 ~ 0x02ffffffprivate final static Integer MIN_VALUE = 0x02100000;private final static Integer MAX_VALUE = 0x02ffffff;static {//生成错误码映射关系init();}/*** 初始化错误码映射关系* * @author danyuan*/public static void init(){codeMap.clear();Integer code = 0;for(BussinessErrors error :BussinessErrors.values()){if(error.getOldCode() == null || (error.getOldCode() == 0)){code = HashUtil.limitELFHash(error.name(), ErrorCodeUtil.MIN_VALUE, ErrorCodeUtil.MAX_VALUE);}else{//兼容老版错误码code = error.getOldCode();}          if(codeMap.containsValue(code)){//说明产生了冲突,打印冲突信息log.error("业务错误码HASH值产生了冲突,请更新错误码名字再启动应用,错误码冲突如下:");log.error("存在值: {} = {}", error.name(), code);System.exit(-1);//直接退出应用} else {codeMap.put(error.name(), code);log.info("业务错误码:{} = {}", error.name(), "0x0" + Integer.toHexString(code).toUpperCase());}           }}public static Integer getCode(BussinessErrors error){return codeMap.get(error.name());}/*** 获取业务错误码集合* @return* @author danyuan*/public static List<ErrorCodeDto> getBussinessCodes(){List<ErrorCodeDto> list = new ArrayList<>();ErrorCodeDto dto = null;for(String key : codeMap.keySet()){BussinessErrors error = BussinessErrors.valueOf(key);dto = new ErrorCodeDto();dto.setError(error.getCode()).setMsg(error.getMsg()).setCode(codeMap.get(key)).setHexCode("0x0" + Integer.toHexString(codeMap.get(key)).toUpperCase());list.add(dto);}return list;}/*** 获取系统错误码集合* @return* @author danyuan*/public static List<ErrorCodeDto> getSystemCodes(){List<ErrorCodeDto> list = new ArrayList<>();ErrorCodeDto dto = null;for(ErrorCodes error : ErrorCodes.values()){           dto = new ErrorCodeDto();dto.setError(error.getMsgCode()).setCode(error.getCode()).setHexCode("0x0" + Integer.toHexString(error.getCode()).toUpperCase());list.add(dto);}return list;}
}
  • 错误码查询接口
/**
* Title ErrorCodeController.java
* Description
* @author danyuan
* @date Nov 19, 2018
* @version 1.0.0
* site: www.danyuanblog.com
*/
package com.danyuanblog.api;import java.util.HashMap;
import java.util.List;
import java.util.Map;import com.danyuanblog.api.impl.response.ErrorCodeListResponse;
import com.danyuanblog.base.exception.BusinessException;
import com.danyuanblog.base.exception.ErrorCodes;
import com.danyuanblog.common.exception.ErrorCodeDto;
import com.danyuanblog.common.util.ErrorCodeUtil;
import com.danyuanblog.web.api.DictManager;import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@Api(tags={"错误码服务模块"},value="相关接口")
public class ErrorCodeController   {@Autowiredprivate DictManager dictManager;@GetMapping(value="errorCodeList",name="查询错误码列表")@ApiOperation(value="查询错误码列表", notes="查询错误码列表")public ErrorCodeListResponse errorCodeList() throws BusinessException{List<ErrorCodeDto> systemCodes = ErrorCodeUtil.getSystemCodes();for(ErrorCodeDto error : systemCodes){error.setMsg(dictManager.get(error.getError(),"zh_CN"));          }return new ErrorCodeListResponse().setBussinessErrors(ErrorCodeUtil.getBussinessCodes()).setSystemErrors(systemCodes);}}
@Data
@Accessors(chain = true)
@ApiModel(value="ErrorCodeDto",description="业务错误码参数")
public class ErrorCodeDto implements Serializable{/** *serialVersionUID*/private static final long serialVersionUID = 1L;@ApiModelProperty(value="业务错误码字典值",example="user.accountNotFound")private String error;@ApiModelProperty(value="系统错误码整型值对应的十六进制值",example="0x06002007")private String hexCode;@ApiModelProperty(value="系统错误码整型值对应的十进制值",example="100671495")private Integer code;@ApiModelProperty(value="错误描述信息",example="该用户账号未找到!")private String msg;
}
@Data
@Accessors(chain = true)
@ApiModel(value="ErrorCodeListResponse",description="获取系统错误码信息的返回参数")
public class ErrorCodeListResponse implements Serializable {/** *serialVersionUID*/private static final long serialVersionUID = 1L;@ApiModelProperty(value="业务错误列表")private List<ErrorCodeDto> bussinessErrors;@ApiModelProperty(value="系统错误列表")private List<ErrorCodeDto> systemErrors;
}

通过这个接口便可以获取整个服务器提供的所有错误码及其含义说明

API接口错误码设计最佳实践相关推荐

  1. API接口安全思考和最佳实践

    一.API安全概述 应用程序编程接口 (API) 允许软件应用程序相互交互.它是现代软件模式的基本组成部分,例如微服务架构. API 安全是保护 API 免受攻击的过程.由于 API 非常常用,而且它 ...

  2. RESTful API 设计最佳实践

    2019独角兽企业重金招聘Python工程师标准>>> 背景 目前互联网上充斥着大量的关于RESTful API(为方便,下文中"RESTful API "简写为 ...

  3. [译] RESTful API 设计最佳实践

    https://juejin.im/entry/6844903503953920007 [译] RESTful API 设计最佳实践 阅读 8779 收藏 0 2017-10-16 原文链接: seg ...

  4. 来自Google资深工程师的API设计最佳实践

    来自Google资深工程师Joshua Bloch的分享:API设计最佳实践 为什么API设计如此重要?API是一个公司最重要的资产. 为什么API的设计对程序员如此重要? API一旦发布,出于兼容性 ...

  5. 四连问:API 接口应该如何设计?如何保证安全?如何签名?如何防重?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:cnblogs.com/jurendage/p/126538 ...

  6. API 接口应该如何设计?如何保证安全?如何签名?如何防重?

    点击蓝色"架构文摘"关注我哟 加个"星标",每天上午 09:25,干货推送! 来源:https://www.cnblogs.com/jurendage/p/12 ...

  7. 对外API接口的安全性设计及鉴权方式

    对外API接口的安全性设计及鉴权方式 API鉴权方式 API Key + API Secret实现API鉴权 Cookie + Session实现API鉴权 token机制实现API鉴权 API接口的 ...

  8. mongodb数据合并设计_「时间序列数据」和MongoDB(二)-模式设计最佳实践

    在上一篇博客文章时间序列数据与MongoDB:第一部分-简介中,我们介绍了时间序列数据的概念,然后介绍了一些可以用于帮助收集时间序列应用程序需求的发现问题.对这些问题的回答有助于指导支持大容量生产应用 ...

  9. python组件的react实现_React-Router动态路由设计最佳实践

    写在前面 随着单页应用(SPA)概念的日趋火热,React框架在设计和实践中同样也围绕着SPA的概念来打造自己的技术栈体系,其中路由模块便是非常重要的一个组成部分.它承载着应用功能分区,复杂模块组织, ...

  10. 软件架构设计最佳实践

    软件架构设计最佳实践 课程介绍: 1.深入阐述软件架构设计的思想.方向及趋势:2.剖析软件架构的全景视图:3.结合实际案例分析架构设计过程及需求对架构的影响:4.如何实用设计模式来实现好的架构:5.实 ...

最新文章

  1. C陷阱与缺陷的学习笔记
  2. 群星巨型计算机事件,群星 三种特殊事件介绍 特殊事件有几种
  3. 2018 ACM/ICPC 沈阳站小结
  4. android 碎片问题,Android碎片问题
  5. 10年老兵给程序员的10条建议! 1
  6. 【转】oracle in和exists、not in和not exists原理和性能探究
  7. 100: cf 878C set+并查集+链表
  8. 谁手握账本?趣讲 ZK 的内存模型
  9. 有人喜欢有人嫉妒,竟然因为这上了互联网热搜
  10. 我的Android进阶之旅------android中一些特殊字符(如:←↑→↓等箭头符号)的Unicode码值
  11. 如何在阿里云上创建安全的远程工作空间
  12. Vue项目JS脚本错误捕获
  13. 微云为您讲述陌陌是如何布局“智慧商城”
  14. eovs实训报告总结心得_实训报告心得体会范文大全
  15. 大数据的数据科学与关键技术是什么?
  16. 唯品会服务器信息,唯品会商品详情信息接口
  17. 智慧民政解决方案-最新全套文件
  18. 90后逝去的青春用代码致敬......
  19. kettle 表输入 显示重复_智能显示仪XMT-F81-00-021A
  20. css html属性,HTML/CSS从零开始-常用属性

热门文章

  1. FastDFS使用之文件名称
  2. 王道2023数据结构笔记
  3. win10重装系统后没有网卡驱动
  4. 31、【栈和队列】判断链表是否为中心对称(C++版)
  5. 多余的回车键(Enter)
  6. HTML+CSS大作业——动画漫展学习资料电影模板(6页) 网页设计作业 _ 动漫网页设计作业,网页设计作业 _ 动漫网页设计成品,网页设计作业 _ 动漫网页设计成品模板下载
  7. Sublime Text 2 设置文件详解转
  8. 处理器后面的字母含义_Intel处理器背盖上的字母含义
  9. 北大元培学院数学与计算机,通识教育试验的尴尬 北京大学元培学院近距离观察...
  10. repo报错:SyntaxError: invalid syntax