主要方式
第一种:区分http状态与业务状态
正常:http -> 2xx
{
code: 200,
message: "成功",
data: object
}
异常: http -> 2xx
{
code: 非200,
message: "错误提示信息",
data: object
}
第二种:http状态与业务状态统一
正常:http -> 2xx
{
code: 200,
message: "成功",
data: object
}
异常: http -> 3xx、4xx、5xx
{
message: "错误提示信息",
.....
}
其实目前来看使用哪一种都能实现相关业务,只要组内达成一致,并没有谁比谁优劣,但是基于目前的职业经历(主营后端,也写过vue、react的一些前端),还是比较推崇第一种方式的。
1.异常处理边界的问题
正常情况下,我们的确需要将异常系统抛出给调用方处理,但是这是单个系统内部方法调用的,目前基于http请求的服务调用,如果简单的将异常抛出,那么就会导致上游系统混乱,无法判定抛出的异常是调用参数不对,还是下游系统狗带了,直接抛出又不符合系统安全要求(如果你的系统不在意安全要求,那么你来讨论这个就没有意义)。
2.使用对象的考虑
系统的使用人是真实的人,提示信息的准确与否同样决定了用户友好度与交互设计的优劣,这也是为什么要对错误进行分类,毕竟准确友好的错误提示不仅能增加产品体验还能快速定位问题,所以到了前端来说,前端就需要区分哪些信息应该展示给客户,哪些信息不能展示给客户,单独的http状态码并不能帮助前端确认是否应该提示(当然,也有人说使用目前http协议中未使用的状态码,此情况后续说明)
从头开始讲
为什么会有http状态码?
这里就不赘述了,需要了解的可以移步超文本传输协议
从网络的七层模型我们可以知道,网络本身并不可靠,超文本协议中定义了各种状态码用来处理一个http请求的各种状态,那么最主要的就有个问题,这个状态码是给谁用的
?
所有的状态描述中,都是在描述C/S模式下的各种状态,那么业务状态是否也是C/S间的状态?
如何选取
通常我们在选择标准的时候总是希望通用与规范,但是实际情况往往是通用的一定大而空,规范的一种小而精,就像物理学界在用一个公式代表所有力一样,程序开发中也是希望有一个规范能代表所有情况,但是目前来看,这种情况不存在,那么如何选取就成了关键点。
易用性
两者相同,不管是第一种方式还是第二种方式,在现有的各种语言中都有通用的处理,所以处理上都不会多很多工作量,如:
java中的的事实通用框架spring提供了ExceptionHandler处理方式
OpenFeign中的ErrorDecoder也能统一处理异常返回
react、vue的插件axios都提供的统一异常返回的处理方式
安全性
严格意义上来说只要后端处理的合适,不管是第一种方式方式第二种方式都能够保证安全性,但是按照以往的经验,如何准确而且不过多的处理异常是个问题。
1、可预期异常:此类异常通常用于业务校验,为了统一格式的处理,我们会在业务代码中显示的抛出异常,统一处理异常提示,便于国际化、异常分类等处理。
2、不可预期异常:通常是因为各种奇奇怪怪的原因导致的非预期异常,比如数据库访问超时、下游服务下线等,既不能预期,也不能放任,通常都会进行统一的异常拦截,防止将堆栈式的异常信息抛给客户。
所以在安全性上来说,只要进行了统一的异常拦截,异常的堆栈信息就不会抛给前端,在安全性上面就符合要求。
为什么会在意堆栈信息呢?因为理论上是不存在的绝对安全的系统的(这个就推荐大家看一部电影《我是谁》),所有的防护手段其实都是在增加破解与攻击的难度与成本,但是如果我们抛出堆栈信息,将本身黑盒的系统,呈现了一部分系统内部结构,降低了攻击方的成本,也就越不安全(参考现在的密码为什么都要大小写字母加特殊符号,以现在的个人计算机性能来说,超过6位的复杂密码的暴力破解基本就无解)。