1
swulling 2022-12-03 21:25:23 +08:00 via iPhone 7
其实也说得通,这种情况就是把 HTTP 当作传输层。类似于 rpc over http 。
|
2
swulling 2022-12-03 21:27:09 +08:00 via iPhone
阿里的 API 是因为内部之前都是 rpc 接口,对外暴露的 HTTP 接口大部分是网关自动转换的。为了避免错误码映射的复杂性,干脆全 200+POST 最简单。
|
3
echo1937 2022-12-03 21:29:27 +08:00 11
我不赞成 HTTP 状态码来代替业务状态码,前者实在覆盖不了后者,
但是我也很难支持“一个 200 打天下”,直接废掉了 HTTP 状态码这种有广大共识的东西。 |
4
mlhadoop 2022-12-03 21:30:30 +08:00
所以最佳实战是怎样的呢?
|
5
swulling 2022-12-03 21:32:30 +08:00 via iPhone 1
|
6
edis0n0 OP @echo1937 #3 但我认为把原始返回全额外封装一层 code: int, data?: object, error?: ErrorDetail 又不方便又浪费资源 欢迎 v 友反驳但要有根据
|
7
Opportunity 2022-12-03 21:34:32 +08:00 1
HTTP 时代的遗留吧,4XX 状态码可能会被代理服务器改内容
|
9
xy90321 2022-12-03 21:36:34 +08:00 via iPhone 23
http status code 反映的是 uri 资源的状态
因此对于一个资源,你是否有权访问 uri 资源跟访问到这个资源后资源内部业务逻辑上的鉴权结果是两码事 所以在我看来,你被 http 服务器权限拦截了返回 http status 403 ,被应用服务器权限拦截了返回 http status 200 加业务状态码是很合理的 |
10
yidinghe 2022-12-03 21:38:18 +08:00 via Android 10
首先,协议层的错误和应用层的错误区分开来是对的。其次,强行要求业务逻辑绑定 HTTP 状态码,而且这个状态码可能会沿着链路传播给非 HTTP 协议的调用,这难道不是坏习惯吗?
|
11
SenLief 2022-12-03 21:40:28 +08:00 via iPhone
我觉得还是区分资源和业务,业务这块返回 200 也正常,毕竟 http 状态码不能完全知道错误在哪,但是请求确实是成功的。所以只要请求成功返回 200 就可以吧
|
12
okakuyang 2022-12-03 21:42:48 +08:00 5
这类问题隔一段时间就会有人问。
对于前端来说,网络连接问题是最优先的,其次才是后端返回的各种数据异常问题,反正异常处理就这样分层了。 请求成功返回 200 ,前端就知道网络请求成功了,然后再去判断这个请求是否业务上成功。 异常提示字符串直接从后端返回 json 里取,这样大大减轻了前端的工作量。 后端只返回几种意义不明显的状态码,前端还要了对照这些状态做不同的显示,没点效率。 |
13
edis0n0 OP @okakuyang #12 后端返回 200 ,前端就直接把返回当作获得的数据,后端返回 40x ,前端就知道遇到错误了,如果返回是 json 就按照 error 来 parse ,否则按照 http 状态码弹出缺省错误信息不是更好吗?
|
15
dddd1919 2022-12-03 21:51:52 +08:00 3
把错误码放在返回结果里,不管是 http 还是内部 rpc 或者其他协议,通用性都更强
另外 http 的非 200 返回在很多框架里定义为异常,各种请求端还要加 try catch ,服务端除了处理业务异常还要考虑 http code ,用的也麻烦 |
16
Iamsonny 2022-12-03 21:52:19 +08:00
没啥问题,公司内部统一规则就行。
|
17
edis0n0 OP @dddd1919 #15 http 的非 200 返回在很多框架里定义为异常 能不能举一个流行框架的例子,我好像还没见到过这种
|
18
jhdxr 2022-12-03 21:57:33 +08:00 3
前阿里移动端路过,你根本无法想象当年(之所以说是当年是因为我离开多年不知道现在如何,并不代表现在好转了)各大运营商等中间网关有多疯狂。
上面说 4xx 篡改内容都算小的,最严重的一次是某地小运营商(可能是为了省网间结算流量)把所有请求都简单粗暴按照 url 缓存了(哪怕是 post 请求),直接导致了当地用户大量串号。 另外小小偏题一句,你标题里已经论断了这是『坏习惯』,那别人出来反驳一下也正常。 |
19
swulling 2022-12-03 22:10:14 +08:00 via iPhone 1
|
21
edis0n0 OP |
22
dorothyREN 2022-12-03 22:23:47 +08:00
有没有可能 200 表示请求成功,自定义的 code 表示操作失败了
|
23
thevita 2022-12-03 22:24:10 +08:00
没这么复杂,也没有谁正确一说,说到底取决于你要传递给调用者的 “语意”
如果你的 API 是标准的 rest ,即 对 “资源” 的 curd ,那么这个时候是能跟 rest 的语意完全匹配的(其实大部分时候都是这样的 api ) 但事上总还是有一些再这之外的复杂一点的逻辑, 要么就 自己在业务模型上封装各种错误,要么就全局封装一个,我理解这是一种在 rest 之上的具有业务语意的扩展而已 --- @swulling 我感觉....,上一个应用我就把服务的错误和资源相关的放 http status ,把 某些业务错误 放 body 了,,这样应用可依据具体情况决定是否处理,怎么处理,当然本身也不一样,前端应用业务逻辑也很重的原因 |
24
bug123 2022-12-03 22:24:22 +08:00
如果返回 500 ,怎么确定是进程挂了,还是进程处理出错了?
|
25
edis0n0 OP @bug123 进程挂了 还是进程处理出错了 这种给运维人员看的东西不是应该写在日志里吗,你总不能把数据库连不上这种信息直接返回给用户吧,所有内部错误对用户来说都是一样的他们解决不了的
|
26
dzdh 2022-12-03 22:34:41 +08:00
- api token 错误
- IP 被拉黑了 - 超过了资源限制 - 试图访问不被授权的资源 按照 http status code 应该统一给 403 的 code (或者有几项应该用 401 和 429 ),但是实际业务中表述不够清晰。 调用方会忽略 http status code (比如我)直接用 response 来进行业务流程的判断。IP 被拉黑了我会判断 response ip rate limited 然后换个 ip 。但是这个表述可能会有变更,我的判断就不生效了,所以业务方会 statuscode:403, body.code:IP_RATE_LIMITED, body.message: ip rate limited 。 然后因为某种资源受限而本次访问不被许可可能会有更上一层的大的集合性表述,那可能是 body.code:FORBIDDEN, body.ErrorCode:IP_RATE_LIMITED, body.Message:ip rate limited. 当然可以用 status403 ,但是从 [调用方便] 我不用从 header+body 里分别取我觉得调用方就很好接受。 业务接口是给人用的,方便才是真理。PHP int+string 我管他啥类型,有结果就行。 |
27
edis0n0 OP api token 错误应该是 401 超过了资源限制应该是 429 吧
|
28
shakukansp 2022-12-03 22:41:20 +08:00
楼主讲一下 token 没过期鉴权失败,没 token ,token 过期
都是 40 几? |
29
edis0n0 OP @shakukansp 403 400 403
|
30
deef 2022-12-03 22:42:57 +08:00
有没有想到问题出在很久以前的 http 时代的运营商头上,当年三大运营商可都是对着 HTTP403 、404 、503 、502 等状态给你返回一个“X 通智能纠错页面”(其实是放广告)你要是碰见这种 IDC ,你还敢用 HTTP 状态码吗?
所以 HTTP 200 没什么问题,表示网络没问题就好了。 |
31
shakukansp 2022-12-03 22:43:33 +08:00
@edis0n0 那鉴权失败但是不跳转登录,token 过期要跳回登录页你怎么区分?还不是要在返回结果里区分吗
|
32
Leviathann 2022-12-03 22:46:37 +08:00
@edis0n0 spring 的 rest template ,相当于一个 http client ,当时我第一次发现这玩意请求返回不是 200 居然用抛异常来表示的时候也是震惊的,什么 nt 设计
|
33
edis0n0 OP @shakukansp #31 发现很多层没理解我的意思,我的想法是少额外包装一层,直接用状态码是否 200 判断是否失败,成功的话整个返回都作为 data 处理,失败的话整个返回结果都作为 error detail 处理,这样更方便资源消耗更少
劫持问题现在基本都有 HTTPS 了吧 |
34
wu67 2022-12-03 22:55:17 +08:00 1
这个问题不是本站早就讨论过了几次吗? 网络请求状态码和业务状态码不要混作一谈, 一个是协议一个是应用业务, 二是业务量上来之后, http 状态码已经不足以表示这么多的业务异常状态了.
所以我也赞成 @swulling 说的 200 + 业务状态码 + 全 post + json/form data, 足够清晰, 并且能有效避免很多 cv boy 瞎复制代码, 例如要给 get 型接口的 body 里带 json 参数, 或者 post 型的参数要全部拼刀 query 上面... |
35
wangritian 2022-12-03 23:15:20 +08:00 8
楼主把自己不认同但其实有相当合理性的方案偏激地归为坏习惯的这种坏习惯不太好
上面很多回复已经指出了网络状态码和业务状态码混用的缺陷了,而拆开用仅仅不符合你的喜好,或者,省几字节流量? |
36
iseki 2022-12-03 23:20:48 +08:00
早期的时候状态码非 2xx 会被篡改,同时没有传输层安全,所以衍生出了这种怪用法。现在很多人改不了,有些前端、后端因为自身水平问题竟然会告诉你不是 200 的就没法统一封装······然后他们还觉得他们这种统一封装的方法挺好,所以就这样了呗(直到现在在某些特殊的场景还会存在 200 劫持行为···)
|
37
iseki 2022-12-03 23:23:11 +08:00
我也支持以 2XX 表示成功,以 4XX/5XX 表示失败,并在失败时内部包含业务用错误码的做法。这样的好处主要是不侵入响应体,可以兼容响应体是 JSON 以外内容的情况,同时也极大规避多态反序列化
|
38
copymaster 2022-12-03 23:27:52 +08:00 via Android 4
什么坏习惯啊,http 状态码 200 说明请求和相应是在系统业务内正常的,返回数据代的状态码是这个系统业务的代码
|
39
ychost 2022-12-03 23:30:05 +08:00
规范统一就好,我个人喜欢仅用 POST/GET ,不用 DELETE/PATCH/DELETE ,在 body 里面包装 code 和 message ,因为 header 的 statusCode 不好扩展,而且仅支持数字,很多时候 code 是个字符串
|
40
so1n 2022-12-03 23:33:31 +08:00
支持全 REST 的就是没有经历过运营商篡改和 CDN 的兼容情况
|
41
iseki 2022-12-03 23:37:21 +08:00
时至今日 HTTPS 已成基本前提,大多数场景不需要再考虑劫持这种稀奇古怪的事情了,为了规避劫持改变系统设计有些得不偿失
|
42
fkdog 2022-12-03 23:38:53 +08:00 5
如果我把业务 code 全都封装进 http entity 里,那么有个好处就是哪天我要把 http 替换成自定义的 rpc 协议,对接方可以用最小的代价来做调整,而不是一行行去找对应的 http status code 。
你们平时玩 clash/gost 不都是这样吗?把传输协议和具体的代理协议给解耦开来,可以方便随意组合配对。哪天 ss 协议要是可以配上 https ,有人非要 ss 协议密码不对的时候返回 http status 403 ,这样才 restful ,你们会不会觉得有病?? 国内这种普遍把业务 code 塞进 http entity 里实际上也是一种不错的设计,纯粹把 http 当成是传输协议。300/400/500 这些用来辅助运维排查问题,对接方只管 status==200 或 400 的。 把 restful 奉为圭臬的通通有点猫饼。当然这一切还是要以公司规范为前提,如果规范是 restful 的那么就按照 restful 来,如果规定业务 code 写进 body 的,那就写进 body 。 |
43
AyaseEri 2022-12-03 23:44:06 +08:00 1
这问题不一定是个技术癖好问题,也可能是个多团队协作、多系统调用、研发运维体系、基建建设与封装等管理问题。
|
44
shakukansp 2022-12-03 23:58:32 +08:00
@edis0n0 nginx 或者 api 网关也会有些默认的返回错误 http 状态码行为,混在一起排查问题的时候会受苦
|
45
xy90321 2022-12-04 00:03:55 +08:00 via iPhone
@edis0n0
200 以外返回内容不一定是 API 给出来的 敢情聊了那么多楼都白聊了呗… 如果你所处的业务环境简单到不需要区分网络协议状态和业务状态,那随便怎么搞都可以 但是也请理解有更多业务环境比这复杂的多 |
46
shakukansp 2022-12-04 00:05:21 +08:00
@edis0n0 换句话说,200 中的错误代码即代表这个 http 请求必然成功通过了 nginx api 网关到达了后端
错误必然是由后端服务返回的 在后端查看日志极其麻烦的情况下有助于快速定位问题出在哪里 |
47
pengtdyd 2022-12-04 00:12:51 +08:00
我觉的这里应该要区分业务状态码和 http 状态码吧。
200 、500 、404 等这些是 http 状态码,它反映是请求和响应情况的状态 00000 、A1000 、B2000 等这些是业务状态码,它反映的是业务执行情况的状态 举例:404 表示这次请求没有找到资源,如果业务执行失败返回 404 ,这根本说不通,这里有歧义:到底是请求本身失败了,还是业务执行失败了。 |
48
Dragonphy 2022-12-04 00:13:43 +08:00
日经贴……
|
49
edis0n0 OP @xy90321 #45 照你这么说 200 状态也不一定是 API 给出来的啊,配置错了反代的时候 200 状态的 API 也有可能被缓存
|
50
thisIsDing 2022-12-04 00:21:09 +08:00 via iPhone 1
遵循 restful 的规范,同时在业务异常的时候加上类似 Error-Source: business 的响应头来标识异常的来源和格式。网关发生异常也可以复用这个头,只要设一个不同的值来区分就好。总之我们是这么做的。
|
51
flyqie 2022-12-04 00:34:47 +08:00 via Android
个人倾向于使用 200 + post + json ,不支持采用 http status code 来同时判断业务和协议的做法。
1. 与 http 协议耦合度较高 2. 不利于错误码拆分 3. 不利于排查,无法直观查看到底是业务还是基础设施问题。 4. 兼容性较差,楼上不考虑兼容性问题的是没被坑过吗。。有些玩意不是你能决定的,遇上了那就算是倒霉,现改业务代码和网关麻烦的要死。 |
53
dcsuibian 2022-12-04 00:39:11 +08:00 via Android
为什么国外的就是好的呢?
|
54
flyqie 2022-12-04 01:05:07 +08:00 via Android
@edis0n0 #52
将技术方案绑定到平台来做比较其实是不太合理的。 1. 不同公司基础设施架构是不一样的。 2. 某些时候技术方案不一定是靠全局优势,还可能仅靠个人喜好或业务优势而被采用。 3.兼容性是根据业务而变的,如果已经完全确认不需要,那当然可以抛弃掉无用的那些兼容性。 归根到底,这其实并不是一个纯技术问题,跟业务和人有分不开的关系。 就从我自己开发中得出的经验来看,200 + post + json 是在大陆(港澳台没深入了解过)普通项目中最有兼容性也最省事的方案,但具体落实哪个还得看实际。 |
55
binux 2022-12-04 01:09:34 +08:00 via Android 3
我一直说,200 一把抓是因为能力不足,无法正确地控制使用上下游组件和框架而偷懒的行为。
|
56
chenxiankong 2022-12-04 01:20:50 +08:00
对 http 状态码,个人看法就是 2xx+非 2xx+业务状态码.错误异常拆分很方便,也不受协议限制.
公司内使用 http 状态码的太多意义反而会提高系统复杂度.怎么方便怎么来. |
58
iseki 2022-12-04 02:40:02 +08:00 1
不存在什么业务异常、系统异常;业务上的冲突,是客户端发起的请求引起的,比如余额不足、用户名冲突等等,4XX ;服务端的异常,比如数据库无法连接、资源不足等等属于故障,5XX
|
59
eason1874 2022-12-04 03:30:51 +08:00
我常跟行家讲,把 HTTP 当传输层协议,状态码不必自己改,拿过来先返回 200 ,接下来 JSON 打包响应体,两转三转,四五六七八转,层层解码层层编码,再下来跟运维打架,让他们自己解包取状态码做监控,这个工程下来,你摸鱼时间起码占一半
|
60
xuanbg 2022-12-04 09:21:25 +08:00
@edis0n0 你认为应该使用 http status 表示请求的状态没错,但事实上,你的请求仅仅是一个原始的 http 请求吗?返回的数据是一个 html 文件吗?显然不是,你这个 http 请求返回的数据除了没有 html 文件,什么样的数据对象都有。因为返回的不是 html 文件,所以,你更应该关注的不是 http 协议的 status ,而是其内含的业务逻辑。http 协议只是用来承载数据的,一般我们都不会关注它的状态,他的状态就应该是 200 ,不然,肯定是网络数据链路发生了故障。
|
61
dddd1919 2022-12-04 09:24:43 +08:00
@edis0n0 httpClient ,okhttp ,以及 springboot 里常用的 feign ,当然可以写回调处理非 200
这些不是重点,重点是用 http code 很难满足在异构系统下完成比较通用的业务表达,况且团队间能力参差不齐,制定统一的消息结构规范避免大家在不同传输协议上做过多纠结 |
63
shakoon 2022-12-04 09:33:17 +08:00
但凡对 OSI 七层模型有一点了解,也不至于会纠结这个问题。所以基本上也能快速分辨出哪些是科班出身的人。
|
64
ByteChen 2022-12-04 09:34:24 +08:00
参考微信支付 v3 API ,restful:
https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_1.shtml |
66
k9982874 2022-12-04 10:28:43 +08:00 via Android 7
你猜它为什么叫 http status code 而不叫 api status code 呢?
看着两个东西长得差不多就搁这硬套,硬套不要紧,还跑出来秀智商。 |
67
LawlietZ 2022-12-04 11:04:41 +08:00
@edis0n0 你现在没有完全的论据证明 stripe twitter 完全是你想的那样的。。。有渠道的话建议你先问问 stripe twitter 内部真正的程序猿再说。。。。
|
68
ccppgo 2022-12-04 13:03:41 +08:00
别人的应用代码你都要专门批判一番, 恶臭爹味了
|
69
bugfan 2022-12-04 13:44:12 +08:00
我在做后端新项目时候,也在想该用 200 包所有自定义的东西呢,还是用 restful 风格来做呢,最后选择用了 restful ,事实证明,光用 restful 远远不够判断业务逻辑的情况,我最后在 2xx 的所有请求里面,自定义的一套状态状态码😂
|
70
6ugman 2022-12-04 13:48:46 +08:00
全用 200 不是没啥经验就是懒,用了 http code 就不能用自己业务状态码吗?懒是可以理解,鼓吹就算了,大家还是多遵守规范的好
|
71
fakeshadow 2022-12-04 14:40:45 +08:00
http 套 rpc 是很好的选择,看 grpc 多流行,没必要片面的否定。
|
72
zr8657 2022-12-04 15:08:16 +08:00
说起这个最近调电信的 5G 平台 api ,结果电信硬是自己编了个 799 的 http 状态码,搞得我封装的好好的代码为了它单独加了一坨处理,真服了
|
73
sprite82 2022-12-04 16:00:48 +08:00
http 状态是传输层状态,反映网络问题,谁知道是不是中间商给拦截了还是用户路由器问题?如果是内部 rpc 调用,我觉得这么搞没什么问题,但也要看状态码能否适应业务复杂度。
|
74
ljzxloaf 2022-12-04 16:18:53 +08:00
http status code 是与业务无关的,业务相关的 status 还是需要通过报文返回,所以我们讨论的其实是业务无关给状态应该如何返回。其实用啥都可以,但是如果用 http status code 的话会增加一些沟通成本和程序员的心智负担,因为要学习和沟通两套 code 。我觉得能 All in one 都尽量 all in one ,看看现在企业的各种平台不都是能集成在一起的都集成在一起了,多转个圈就多了一分成本。
而且现在有些公司用 http 是对外的 web 接口,有些公司则直接用 http 作为 rpc ,后者如果为了以后升级方便,应该尽量不要依赖 http status code ,http 仅作为传输层使用。 |
75
hlayk 2022-12-04 16:45:41 +08:00
https://www.v2ex.com/t/808610 之前也讨论这个问题
|
76
alamaya 2022-12-04 17:57:36 +08:00
什么 http 原教旨主义
|
77
zealinux 2022-12-04 18:45:02 +08:00
使用 JMeter 进行压测的时候,全是 200 成功,其实打开后发现大多数多数其实是错的。
|
78
vagusss 2022-12-04 20:02:14 +08:00 via iPhone
争来争去,谁也说服不了另一方,我觉得只要有规范约定清楚,用啥都行,个人更倾向于 200+post+json+code
|
79
zsdroid 2022-12-04 21:00:18 +08:00 1
发现 op 非常偏激。twitter 这些国外平台用的一定是对的。国内不是这么用的一定是错了。
|
80
janus77 2022-12-04 21:06:41 +08:00
200 是状态码,不是结果码……连接是正常返回就可以叫做状态 200 ,至于结果,那可复杂多了
|
82
edis0n0 OP @zaunist #80 为什么不可以,我是想用状态码代替原来的 success: bool ,如果不想区分太细 200 和 400 就够用,只是 40x 还可以再细分而已
|
83
iseki 2022-12-04 21:28:32 +08:00 1
有些人总喜欢不管成功与否皆返回 200 ,于是为了鉴别成功失败(会对应不同的响应体 schema ),必须给所有请求的响应体包裹一个 wrapper ,彻底侵入响应体,既不考虑违反 HTTP 带来的麻烦(监控等等),还导致响应体必须是个 JSON (不然没法包裹了),美其名曰方便区分「系统异常」「业务异常」,实际上连故障和错误都没划分明白(这里点名批评 Java 的 Exception 类型)。
更有甚者,包就包了,他还把成功和失败时的内容载荷给你丢到 wrapper 的同一个字段里,导致必须先把 JSON 解析一次,知道了是成功还是失败,才能用正确的类型把载荷再映射一次,病得不轻。 |
84
holinhot 2022-12-04 21:28:49 +08:00 1
http 状态码用来表述协议链路状态(例如 503 504 400 ),自己 body 里定义的状态码用来表述业务逻辑的处理状态。
两种本来就不冲突,现实是很多人拿来混在一起,故障排除时反而不好弄。 |
85
night98 2022-12-04 23:11:58 +08:00 1
|
86
securityCoding 2022-12-04 23:29:17 +08:00 via Android
一个是协议,一个是业务很难理解吗? rpc 上网关不用业务码用什么?业务千奇百怪各种状态码需要定义和透传
|
87
akira 2022-12-05 00:11:04 +08:00
200 表示系统一切正常,这样是不是好理解很多了
|
88
IvanLi127 2022-12-05 00:20:08 +08:00 via Android
这个坏习惯导致各自闭门造车,不知道为什么有人喜欢浪费现有资源,自己再套一层。那群以 http 状态码不够覆盖业务错误的,想想自己是不是被 http 限制了想象力!而且在 HTTP 上封装自己的协议后,居然不提供相关工具链,简直就是重复劳动,瞎卷。
异常响应没必要和正常相应走同一套逻辑,给正常响应套一层完全没意义。还有,HTTP 标头那么好的东西,不要浪费! |
89
vitoaaazzz 2022-12-05 09:25:56 +08:00
http 状态码的表达力有限,不是 200 打天下就是 200+400 打天下,很多错误你确定能找到对应的状态码?就算用了少部分意义适当地状态码,不还是要在返回结果里再定义错误码?
|
90
knives 2022-12-05 19:42:25 +08:00
月经贴……然后我也回过几次类似的回复:为什么不全都要呢 :doge
状态码和响应体一起用并不冲突。Http 状态码用来区分正常响应和错误响应,响应体中给出具体的错误码。当然,200 状态就没有错误码这一封装了。 也许这有点多余的认知成本,但实际上用得最多的就三状态:200 (这个甚至不用写),400 (用户输入错误),500 (服务端错误)。这不比各种业务面条来得复杂吧…… 再进一步说,新的 Spring6 开始支持 RFC 7807 规范: https://www.rfc-editor.org/rfc/rfc7807 ,规定了异常信息如何返回。可参考: https://www.51cto.com/article/721281.html 。基本思路就是异常产生时的响应特殊 header 和 json body 。 当然以 Java17 的实际普及程度来看广泛要升级到 Spring6 还有相当长的时间,但我已开始期待,观望等 Spring6 普及了这一月经贴的内容是否会有所变化😀 |
93
lizy0329 174 天前
混合用最好,讲真,你封装时候,无论是 外面的 status 还是里面的 code ,都需要处理
|