Code状态码是界面设计中常用的概念。本文主要讨论界面开发中的代码设计。从客户端和服务端开发的角度,给出了具体的工程实践建议和思考。从笔者之前的接口文档定义开始,文档中定义的服务端接口的输出格式如下。接口输出格式返回的数据由两部分组成。第一部分是结果集的描述,第二部分是数据节点{"code":4302,"message":"nosign","time":1487832032,"data":[]}第一个部分,不管有没有错,都会有下面的片段。code:信息codemessage:信息descriptiontime:接口返回时间第二部分是具体数据如下:datanode我们可以看到code=4302,4302不是HTTP协议状态码,而是业务状态码,是thebusinessfield的意思不是我们常见的HTTP协议层面的响应状态码。业务状态码和HTTP状态码在REST接口设计规范中,通常会引导我们这里的Code应该是HTTP协议状态码200、404或者501等,其实这是一种在实践中折衷的方法。代码将包括HTTP状态代码和业务状态代码。业界为什么存在这种做法,与客户端解析数据的方式有很大关系。下面会给出答案。说到这里,我们引入了两个概念,一个是业务状态码,一个是HTTP请求状态码。这两个概念很好理解。业务状态码是服务器给出的关于业务描述的代码,用于客户端清楚地知道本次请求资源的状态。上例中的4032被认为是缺少签名的业务状态代码。输出一个业务状态码表示当前HTTP请求成功。业务状态代码是可变的,没有行业标准。是资源状态描述,与HTTP响应状态码没有对应关系。如下图HTTP-200,接口连接上,HTTP状态响应返回200,但是业务没有执行成功,代码用1表示。HTTP/1.1200OKServer:nginxDate:Wed,13Nov201901:27:03GMTContent-Type:application/json;charset=UTF-8Transfer-Encoding:chunkedConnection:closeX-Powered-By:PHP/5.6.15Access-Control-Allow-Origin:*Access-Control-Allow-Methods:POST,PUT,GET,DELETE,OPTIONSAccess-Control-Allow-标头:令牌、应用程序密钥、内容类型、etcp-baseAccess-Control-Max-Age:86400X-Frame-Options:SAMEORIGINX-Content-Type-Options:nosniff{“code”:1,“message”:“statesiswrong","data":[]}HTTP状态码HTTP请求状态码是HTTP协议的一部分,用来表示HTTP响应状态。HTTP状态码是HTTP协议的工程实现,服务端实现不符合协议。我们可以认为服务器的HTTP实现有误。这是一个幂等性的简单例子。我们知道DELETE方法是幂等的。如果某个资源之前被删除过,再次请求时应该返回200响应码,而不是该资源不存在的404响应。服务端开发实战为什么要强调上面两种状态码的分类,是因为在行业的发展中,这两种代码会互换使用,有特定的使用场景,语义不能混淆。这里有几个问题:如何使用Code代码表示连接成功如何使用Code代码表示访问已经达到客户端预期的结果客户端应该收到HTTP状态码还是业务状态码?客户端HTTP请求先这篇文章对客户端的简单定义就是调用服务端接口的调用者主要是前端WebView,Android和iOS工程师,统称为大前端。前端WebView请求会涉及跨域CORS。其实简单来说,客户工程师最关心的有两个问题:第一,接口是否连通。第二,接口是否返回我想要的数据。有经验的客户端工程师会关心如果接口不可达,返回提示是否能指导我排查错误,或者追查问题。其次接口设计是否合理,是否存在隐患,就要看工程师的专业水平和敬业精神了。客户端排除法客户端HTTP请求的一般方法是使用排除法,什么是排除法,当客户端请求服务端的REST接口时,首先会判断该接口是否在网络层面连接,包括404或200.客户端只关心有用的代码本身,其余的作为异常处理。网络层判断客户端会将这个任务交给特定的HTTP拦截器(Intercept),然后接受当前界面的描述信息,即数据和代码,进行业务前端处理。axios是一个HTTP客户端,主要用于浏览器请求,包括请求响应拦截器(Interceptrequestandresponse)PromisebasedHTTPclientforthebrowserandnode.js以下代码是两个响应拦截,分别拦截HTTP协议401validationfailed自定义业务代码验证失败。HTTP401axios.interceptors.response.use(function(res){returnres.data;},function(res){letresponse=res&&res.response||{};if(response.status==401){tool.showToast('登录已过期,请重新登录。');tool.removeReUserInfo();location.hash="#/login";}else{tool.showToast('请求数据失败,请稍后重试。');}});自定义业务代码axios.interceptors.response.use(function(res){Indicator.close();//先关闭所有提示窗口vardata=res.data;if(data.code==4034){//签名isinvalidtool.showToast('签名无效。');}elseif(data.code==4033){//令牌失效工具.showToast('登录已过期,自动登录。');工具.removeUserInfo();location.hash="#/登录名";}else{返回数据;}},function(err){//alert(JSON.stringify(err));指标.close();//先关闭所有提示窗口tool.showToast('请求数据失败,请稍后重试。');});Android客户端拦截器okhttp是Android平台的HTTP客户端,它包含一个网络拦截器(NetworkInterceptors)。网络状态码和业务状态码的拦截交给拦截器处理。DesignAdvocacy这里我们把之前提出的三个问题重新梳理一下,给出一些解决方案,总结一些经验。如何用Code表示连接成功?这个应该是根据HTTP状态码,主要是200、401,表示请求是【触及数据处理的业务部分】比如HTTP/1.1200OK{"code":0,"message":"Customer客户端是最新版本","data":{"code":10},"debug_stack":[]}HTTP/1.1401UnauthorizedServer:nginx{"name":"Unauthorized","message":"Tokenisexpired","code":401,"status":401,"type":"yii\\web\\HttpException"}如何用Code表示本次访问达到了客户端预期的结果?这里根据业务状态码的数据,得到的是真实的。代码可以用0表示。{"code":0,"message":"客户端是最新版本","data":{"code":10},"debug_stack":[]}客户端应该收到HTTP状态码还是业务状态优先代码?当然是先接收HTTP状态码,再接收业务状态码,既不混淆也不混淆。从软件分层来看,接收HTTP状态码是在接收业务状态码的上层,通常由拦截器来完成,比如token过期时的401阻塞。一般来说,0表示成功,1表示业务运行失败。当业务复杂时,需要维护多个业务状态码。下图是微信平台业务状态码的枚举,场景很多。界面字段整洁。这里所说的字段整洁是指服务商提供的数据结构是完整的,也是最通用的。现在大部分接口格式应该有以下三个字段,可以为空,避免NULL。{"code":200,"data":null,"message":"success"}对于提供接口开发的服务商,code和message字段都要给,反感字段是data。更准确的说法是请求的资源描述包含资源状态码和描述信息,比如message。当data里面没有数据的时候,有些工程师喜欢把data设置为null,或者直接不返回data字段。这两种方式都不合理,会增加调用者的判断成本,尤其是null。如果调用者写得不严谨,很容易造成程序异常。接口总是有返回值的,data字段就是实际的返回值,可以是空字符串,空数组,也可以是bool类型。图灵奖获得者TonyHoare曾公开表示null是一个糟糕的设计,null总是代表着不确定性。业务状态码不等于异常业务状态码和异常是两个概念,切忌混淆。业务状态码是指正常业务处理结果的显示说明,异常通常是由于语法错误和数据缺失导致程序无法正常完成。业务状态代码无法阻止异常。总结本文从接口文档入手,介绍状态码的概念,状态码又细分为网络状态码和业务状态码。结合服务端和客户端的编程角度,介绍各自的使用场景。在分布式面向服务的网络架构中,清晰的网络状态码和业务状态码有助于服务链路跟踪和业务链路跟踪,尤其是异常定位和捕获。服务状态码应该趋于统一,与网络状态码相辅相成。参考文档中给出了一些资源,有兴趣的读者可以参考阅读。参考文档axios-interceptors-response-undefined
