专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序员小灰  ·  3个令人惊艳的DeepSeek项目,诞生了! ·  13 小时前  
程序猿  ·  “我真的受够了Ubuntu!” ·  2 天前  
程序猿  ·  “未来 3 年内,Python 在 AI ... ·  3 天前  
51好读  ›  专栏  ›  SegmentFault思否

系统服务化构建 - 状态码设计要点

SegmentFault思否  · 公众号  · 程序员  · 2020-02-14 12:01

正文

本文转载于 SegmentFault 社区
社区专栏:图南科技
作者:needrunning009


Code 状态码码是接口设计中的常见概念,本文主要讨论接口开发中 Code 码设计。从客户端和服务器端开发的角度,给出具体的工程实践建议和思考。

从一份接口文档定义开始说起,文档中定义的服务端接口输出格式如下




接口输出格式



返回数据由两部分构成,第一部分是对结果集的说明,第二部分是 data 节点

{
"code": 4302,

"message": "no sign",

"time": 1487832032,

"data": []
}
第一部分,无论错误与否,都会有如下片段。

code:信息代号
message:信息描述
time:接口返回时间

第二部分是具体数据如下:
data 节点

我们可以 看到 code=4302,4302 并不是一个 HTTP 协议状态码,而是一个业务状态码,是业务领域的含义,并非我们常见的 HTTP 协议层面的响应状态码。





业务状态码与 HTTP 状态码



在 REST 接口设计规范中,我们通常都会被引导为这里的 Code 应该是 HTTP 协议状态码 200,404 或者 501 等。

实际上这是实践中的一种折中的方式,Code 会包含 HTTP 状态码和业务状态码

业界为什么会有这种实践,与客户端的解析数据方式有很大关系,下文中会给出答案。

说到这里,我们引出了两个概念,一个是 业务状态码 ,一个是 HTTP 请求状态码

两个概念很好理解

1. 业务状态码


状态码对应.jpg

业务状态码是服务端给出的关于业务描述的码,用于客户端明确得知本次请求的资源的状态情况。上文例子中的 4032 被认为是一个缺少签名 sign 的业务状态码。有业务状态码输出表明当次 HTTP 请求是通的。

业务状态码是可变的,没有业界标准,是一种资源状态描述,与 HTTP 响应状态码也不存在对应关系。

如下文图片 HTTP-200 显示,接口是通的 HTTP 状态响应返回 200,但是业务没有执行成功,code 用 1 表示。

HTTP-200.png

HTTP/1.1 200 OK
Server: nginx
Date: Wed, 13 Nov 2019 01:27:03 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
X-Powered-By: PHP/5.6.15
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, PUT, GET, DELETE, OPTIONS
Access-Control-Allow-Headers : token, app-key, content-type, etcp-base
Access-Control-Max-Age: 86400
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff

{
"code": 1,
"message": "states is wrong",
"data": []
}


2. HTTP 状态码


HTTP 请求状态码是 HTTP 协议的一部分,用于表明 HTTP 响应状态。

rest 响应 401.png

HTTP 状态码常见的有 200,404,501 几个。

HTTP 状态码是 HTTP 协议的工程实现。如果服务器端的实现不符合协议的规定,我们可以认为 服务器的 HTTP 实现是错误的。

这里举一个简单的幂等性例子,我们知道 DELETE 方法是幂等的,如果之前已经删除过特定的资源,再次请求时也应该返回 200 的响应码,而不是 404 资源不存在的响应。

3. 服务器端的开发实践


为什么上文中着重介绍状态码的两种分类,因为在业界开发中,这两种码会交叉使用,都有具体的使用场景,语义上不应该被混淆。

这里抛出几个问题:

如何用 Code 码表明此次访问是连接成功的
如何用 Code 码表明此次访问达到了客户端预想的结果
客户端应该先接收 HTTP 状态码还是业务状态码

4. 客户端 HTTP 请求

先对本文中的客户端做一个简单定义,即调用服务器端接口的调用者,主要是前端 WebView,安卓和 iOS 工程师,统称大前端。前端 WebView 的请求会涉及到跨域 CORS

其实简单来说,客户端工程师最关心两个问题:

第一,接口有没有通。
第二,接口有没有返回我想要的数据。

有经验的客户端工程师会关心接口如果不通,返回提示是否可以指导我排除错误,或者说跟踪到问题所在。接下来接口设计是否合理,是否有隐患,就看工程师职业水平和职业素养了。


5. 客户端排除法


客户端 HTTP 请求的通用方法是采用排除法,什么是排除法,客户端在请求服务端的 REST 接口时,会先在网络层面判断接口是否通,包括 404 或者 200常见几个状态 。客户端只关心本身有用的 Code,其余都按丢弃处理。

网络层判断这个任务客户端会交给具体的 HTTP 拦截器 (Intercept) ,之后才会接受当次接口的描述信息也就是 data 和 code,做业务前端处理。

axios 就是一个主要用于浏览器请求的 HTTP 客户端,包含请求响应拦截器 (Intercept request and response)

Promise based HTTP client for the browser and node.js


以下代码是两段响应拦截,分别是拦截 HTTP 协议的 401 验证不通过,自定义业务代码的验证不通过。

HTTP 401


axios.interceptors.response.use(function (res) {
return res.data;
}, function (res) {
let response = res && res.response || {};
if (response.status == 401) {
tool.showToast('登录已过期,请重新登录。');
tool.removeReUserInfo();
location.hash = "#/login";
} else {
tool.showToast('请求数据失败,请稍后再试。');
}
});


6. 自定义业务代码

axios.interceptors.response.use(function (res) {
Indicator.close(); //首先关闭所有的提示窗口
var data = res.data;

if (data.code == 4034) { //签名不合法
tool.showToast('签名不合法。');
} else if (data.code == 4033) { //token失效
tool.showToast('登录已过期,自动登录中。');
tool.removeUserInfo();
location.hash = "#/login";
} else {
return data;
}

}, function (err) {
// alert(JSON.stringify(err));
Indicator.close(); //首先关闭所有的提示窗口
tool.showToast('请求数据失败,请稍后再试。');
});

安卓客户端拦截器


okhttp 是一个安卓平台的 HTTP 客户端,其中包含一个网络拦截器 (Network Interceptors) 。网络状态码和业务状态码的截取都交给拦截器处理处理。





设计倡导



这里重新梳理之前提出的三个问题,给出一些解决思路,同时总结一些经验

如何用 Code 码表明此次访问是连接成功的?


这里应该以 HTTP 状态码为依据,主要有 200, 401 ,表明请求是【触碰到关于的数据处理的业务部分了】如

HTTP/1.1 200 OK
{
"code": 0,
"message": "客户端已是最新版本",
"data": {
"code": 10
},
"debug_stack": []
}
HTTP/1.1 401 Unauthorized
Server: nginx

{
"name": "Unauthorized",
"message": "Token is expired",
"code": 401,
"status": 401,
"type": "yii\\web\\HttpException"

}


如何用 Code 码表明此次访问达到了客户端预想的结果?


这里以业务状态码的数据为依据,获取到的就是真实的。Code 可以用 0 表示。

{
"code": 0,
"message": "客户端已是最新版本",
"data": {
"code": 10
},
"debug_stack": []
}

客户端应该先接收 HTTP 状态码还是业务状态码?


当然是先接收 HTTP 状态码,其次是业务状态码,不混淆,也不能混淆。从软件分层的角度来说,接收 HTTP 状态码在接收业务状态码的上层,通常由拦截器来做,比如 token 过期的 401 阻挡。

一般情况下,0 表示成功,1 表示业务操作失败。业务复杂时,需要维护多种业务状态码。下图是微信平台的业务状态码枚举,场景较多。

微信错误码.png

接口字段整齐


这里所说的字段整齐是指服务提供方给到的数据结构是完整的,最通用的,现在大部分接口格式如下

三个字段应该都存在,可以为空,避免 NULL。
{






请到「今天看啥」查看全文