• 请不要在回答技术问题时复制粘贴 AI 生成的内容
dunhanson
V2EX  ›  程序员

为什么要区分不同的 http 状态码?想说服同事

  •  3
     
  •   dunhanson · Apr 13, 2022 · 19700 views
    This topic created in 1517 days ago, the information mentioned may be changed or developed.

    我的个人的理解还是,这么做比较规范

    但是同事的理解更多是优点好处是什么

    比如用户登录错误之前的方式都是返回 http 状态码 200

    {
      "code":4001001001,
      "message":"用户登录失败"
    }
    

    现在按照规范应该是,返回 http 状态码 401 ,然后 json 还是老样子

    Supplement 1  ·  Apr 13, 2022

    首先非常意外,这个话题确实挺有争论的,评论太多了,抱歉不能一一回复。

    公司太多项目各种问题,都是由于没有一个统一的规范,导致每个人都按自己的理解去写代码。

    现在有个新项目,所以想定制一个规范(命名规范、异常处理、RESTful等等),大家都按这个规范来,避免一些以前的老问题,写出那些不好维护的代码。

    当然我个人还是偏向于区分不同的HTTP状态码,而不是200一把梭哈。

    其实又能引出另外一个话题,为什么POST一把梭哈的问题 🤣

    当然这个同事们已经有共识了,确实区分会好点

    176 replies    2022-04-15 10:40:00 +08:00
    1  2  
    wu67
        101
    wu67  
       Apr 13, 2022
    月经帖....
    目前一种比较常见的方法, 是 http code 管 http 请求状态; json 里面的 code, 管该请求的业务状态, 两个别混作一谈.

    就目前国内应用乱七八糟的业务场景而言, http 状态码的使用, 很难覆盖到大部分场景, 那还不如直接 http200 一把梭, 靠业务 code 区分, 其他非 http200, 前端全部当成出错处理
    sjdhome
        102
    sjdhome  
       Apr 13, 2022
    只用 200 然后自己定义状态码无非是把 HTTP 当成传输层协议来用了,然后自己发明一套应用层协议。如果真有自己定义的必要那就直接重新定义新的应用层协议好了,别用 HTTP 套娃了。
    TimPeake
        103
    TimPeake  
       Apr 13, 2022
    散了吧 引战贴🤣
    ktqFDx9m2Bvfq3y4
        104
    ktqFDx9m2Bvfq3y4  
       Apr 13, 2022 via iPhone
    @sjdhome
    严格遵守必有所失。比如下单前检查是否能下,是不是应该使用 get ?那么多参数对象数组嵌套你非用 get 折腾?某些场景允许 404 ,你非给我返回错误码,一堆可以接受的 404 不会给运维带来麻烦?

    http 传输层错误和业务层错误分开,有那么不堪一击吗?
    iyaozhen
        105
    iyaozhen  
       Apr 13, 2022
    @3dwelcome 监控是全方面的

    你说的单独写 API 是探针那种?
    业务上来说一般是日志监控(已有关键字、新增错误等等方式),还有自动化拨测
    iyaozhen
        106
    iyaozhen  
       Apr 13, 2022
    @adoal 哦哦,那认同你的观点。「为了方便监控离校系统调用图书馆系统接口时的业务逻辑错误率而想把我千奇百怪的业务错误码从 JSON 里提出来放到 HTTP 状态码吧。」这种是不支持的
    这种就是业务自己日志监控或者埋点上报了
    xuanbg
        107
    xuanbg  
       Apr 13, 2022
    @sjdhome HTTP 是 HyperText Transfer Protocol 的缩写,翻译过来就是超文本传输协议,本来就是传输协议,怎么就不能当传输协议用?
    dunhanson
        108
    dunhanson  
    OP
       Apr 13, 2022
    @ClericPy 确实,无论如何,统一比较重要
    lshero
        109
    lshero  
       Apr 13, 2022
    我觉得最好还是提前沟通一下
    因为接口的改动涉及的上下游不只自己一人,也许客户端不想在异常捕获里处理业务逻辑。
    也许运维团队就想监控一下 status code ,判断一下服务是否正常,压根不关心你的业务逻辑对不对
    stroh
        110
    stroh  
       Apr 13, 2022
    说个地狱笑话:老板没给超过 post 返回 200 多余的工资
    ----来自一位曾经月薪 30k 的同事
    yedanten
        111
    yedanten  
       Apr 13, 2022 via Android
    业务层返回的状态码一律 200 ,这才符合规范好吧,不然因为授权失败返回 401 到底是业务层出的问题还是 nginx 之类的 web 服务器配置问题亦或者是路由网关层拦截?而且按规范,你业务层都处理玩业务逻辑了,就是说明请求的资源是存在并且有业务处理结果了,当然是 200 啊
    Nich0la5
        112
    Nich0la5  
       Apr 13, 2022
    以前因为用 40x 导致 nginx 误以为宕机,后来就干脆全 200 了
    fkdog
        113
    fkdog  
       Apr 13, 2022   ❤️ 1
    其实原因在与,以前的 http 是用来传输超文本一类的资源的。现在应用复杂化,http 职责下沉,用来当成数据的封包协议。
    很多大公司甚至连 location 都不用了,把服务名当成参数丢进 request body 交由后端路由。

    陈皓的文章我看过,我觉得他说的也有道理。但是想想陈皓他本身是一个优秀的开发者,与他共事的人肯定也是很优秀。因此他很难想象在一些小公司里,水平良莠不齐的开发的想法是有多奇葩。国内的开发人员普遍缺乏工匠素养,外加国内的氛围就是抢用户市场、赚快钱,因此稳定的架构、夯实的基础并没有太多的价值。

    我觉得,人应该灵活一点。面对水平低的同时,你花时间去和他 argue & debate 没有意义,不如直接是是是,能忍则忍,不能忍直接润。
    bobo2
        114
    bobo2  
       Apr 13, 2022
    都返回 200 ,错误日志不就不存在了??那出问题怎么查日志?
    EastLord
        115
    EastLord  
       Apr 13, 2022
    @yedanten 哪里有状态码一律 200 的规范,学习一下
    lmmlwen
        116
    lmmlwen  
       Apr 13, 2022   ❤️ 3
    我看这里面一堆理论家,说状态码这好那好,但有人用吗,估计很少,因为会造成诸多不便,基本都是理论式编程
    byte10
        117
    byte10  
       Apr 13, 2022   ❤️ 1
    @Biwood 哈哈 用 404 劫持页面,这个鸡贼😂
    @adoal 可以的 666 ,收录你的回答,以后可以用这个理由。
    @BeautifulSoap 同意。
    @1000copy 挺好的,收录你这些理由。
    @alswl 同意,收录,单纯把 http 当做传输协议也未尝不可,尤其是在 app 端是更好了。
    @adoal 挺好的分析
    @MrSheng 😂,可以的。
    @salmon5 真实了。
    @3dwelcome 可以的,案例不错。
    @lisongeee 哈哈跨界了
    @coala restful 规范确实很难执行
    @seakingii 我不喜欢 restful ,都是太过于理想了。

    总结一下:业务服务还是 200 一把梭好,没有啥原因,把 http 当成一个传输协议,因为有可能用 mqtt ,也可能用 ws 。听话不然就会很惨的,以后会很惨,前端后端都惨。跟那个 restful 一样,最后四不像。还是把事情简单化吧,不然以后不会幸福的。
    casxter
        118
    casxter  
       Apr 13, 2022
    其实这是个公司历史遗留问题,公司用啥就用啥
    mydingyan
        119
    mydingyan  
       Apr 13, 2022
    @zmal 点进去看看误打误撞竟是左耳朵耗子的博客,前几天通过 Qecon 看他的直播
    skiy
        120
    skiy  
       Apr 13, 2022
    因为状态码不够用?毕竟只取状态码无法判断是什么错误。
    ktqFDx9m2Bvfq3y4
        121
    ktqFDx9m2Bvfq3y4  
       Apr 13, 2022
    @bobo2
    错误日志 Debug 日志,都有日志层统一处理,各开发语言都有这样的框架。你不能指望错误日志去 Nginx 的 Log 看吧?
    dwlovelife
        122
    dwlovelife  
       Apr 13, 2022
    很多人把状态码和业务码混淆来说了,首先 HTTP 状态码理所应当交给服务器去判断,这次请求到底是 200 还是 400 还是 500 ,难道你要在代码里因为业务问题,恰巧这个业务问题像是 400 请求参数错误,所以我给客户端返回个 400 ,看!你因为主观的判断修改了 HTTP 状态码,此刻它已经不纯粹了它到底代表 HTTP 请求的状态还是你业务的一种场景已经区分不了啊,所以业务码它就是业务码,HTTP 状态码就应该让服务器本身去决策
    dwlovelife
        123
    dwlovelife  
       Apr 13, 2022
    再返回楼主的例子,它是一个登录错误,OK, 登录错误有很多种服务未授权 [类似于在黑名单] 、token 失效、秘钥不匹配,此时难道都要以 401 去返回给客户端么,那么这个 401 到底代表什么?倒不如定义一种业务码未授权比如是 1001 ,token 失效是 1002 、秘钥不匹配 1003, HTTP 状态码依然是 200, 为什么是 200 因为本次的请求是成功的它是服务表述的状态,而登录的未授权需要根据场景返回对应的业务码。个人理解
    darknoll
        124
    darknoll  
       Apr 13, 2022
    问就是 post 200 一把梭
    PEAL
        125
    PEAL  
       Apr 13, 2022
    都可以,全局统一就行了
    est
        126
    est  
       Apr 13, 2022
    RESTful 一时爽,nginx log 统计拆路径、状态码火葬场
    xfriday
        127
    xfriday  
       Apr 13, 2022
    我说两点,
    1. 把 code 放 body 里,除非 body 又是一个自定义的超文本协议,像 json 这种文本“协议”(包含通用 code ,data 可以理解为一种协议了)没法包非文本数据(你 base64 编码下当我没说)
    2. 如果需要根据 code 动态序列化 data ,那反序列化要 2 次完成
    xfriday
        128
    xfriday  
       Apr 13, 2022
    而 http 本身就是超文本协议,body 可以是文本,也可以是图片等二进制数据,status code 是在 header 里,而且是简单的基于行的协议,不需要反序列化就可以读取 status code ,根据条件解析 body
    ryh
        129
    ryh  
       Apr 13, 2022
    除了 4** 其他的真的是不适合用,而且不包括 404
    你 404 了,谁知道是 API 端口没了还是没 query 到
    rv54ntjwfm3ug8
        130
    rv54ntjwfm3ug8  
       Apr 13, 2022
    测了下各大公司情况 v2ex.com/t/846785
    zlo309618100
        131
    zlo309618100  
       Apr 13, 2022
    基于 httpcode 来实现的状态管理,我们的扩展性如何?
    比如 502 ,就是服务器挂了呀,然后再在这上面加个业务含义。
    怎么玩呢?
    ecnelises
        132
    ecnelises  
       Apr 13, 2022
    联想到了吗,对 HTTP 是否应该使用状态码来表达业务状态的争议,其实颇像「使用异常还是错误码」这个同样老生常谈的问题。有些人大小事都要抛异常;另一些人尝试 catch 一切异常,并永远使用错误码。而当代码要接入一个现有模块时,事情变得更复杂了。

    HTTP 通用的错误码( 4xx, 5xx )很有限,就像系统定义的异常类型,必然无法涵盖所有错误的情况。但我们不必做二极管,始终在上面提到的两种人间反复横跳:尽管很多错误码是和 HTTP 协议强相关的(比如 411 ),至少常用的 401/403/404 语义可以完美对应项目中一定会出现的几种错误情况;除此之外,简单用 400 表达请求有问题,500 表达服务端有问题,搭配自定义的错误代码即可。

    另一个和是否使用 HTTP 错误状态码相似的争议,是「是否要以 REST 方式组织 API 」。是的,生搬硬套的 CRUD 语义没办法定义现实中的所有复杂情况,但也不该因此就彻底抛弃这套被广泛接受的代码组织思路:GET 不应有副作用,PUT 相比 POST 有幂等性;实在不行把一些操作都抽象成某个 Action ,然后所有有副作用的操作一律 POST 也比一团乱麻来得整洁。
    luckyc
        133
    luckyc  
       Apr 13, 2022   ❤️ 1
    没什么好说的, 全是 200, 返回的数据必包含 code, 根据 code 判断.
    好处是错误码可以自定义 n 多个. Http 状态码才几个.

    {"code":-1001, "msg":"登录失败, 用户名或密码错误"}
    {"code":0, "msg":"ok","data":{"user":{"name":"test"}}}
    afewok
        134
    afewok  
       Apr 13, 2022
    盲猜,肯定有添狗说,国内不懂规范,国外严格遵守。并举例说,不遵守引发的问题。。
    ipuhua
        135
    ipuhua  
       Apr 13, 2022 via iPhone
    先用 http 状态码,业务上能行就继续。不能行自然就转了 200 一把梭了。我司是后者。

    最大的转折点在于 httpcode 的数量太少无法应对复杂的业务状态。
    kaiki
        136
    kaiki  
       Apr 13, 2022
    说个很简单的,一个是别人已经制定好的规则,一个是自己制定的规则,使用一个就行,两个都用不行。
    前端后端都是自己做的话,哪个方便就哪个。
    Soin
        137
    Soin  
       Apr 13, 2022
    http 状态码数量太少,只能用于对出错情况进行分类
    具体的错误还是需要使用业务状态码再次拆分
    并且,http 状态码每种只用一个( 2xx ,4xx ,5xx )自定义的跟服务器返回的不冲突,这样也可以区分到底是服务器出问题还是业务出问题,至少可以解决接入监控的问题
    dr1q65MfKFKHnJr6
        138
    dr1q65MfKFKHnJr6  
       Apr 13, 2022
    面试面了几个,有外包经验的基本都是 200 一把梭。。。。简单来说就是图省事。
    但凡规范化一点的,都是要区分几种基本的状态码。
    200 ,400 ,401 ,403 ,500 等, 具体细化各有差别。还有说 http 状态只是给浏览器看的, 简直 2
    liuidetmks
        139
    liuidetmks  
       Apr 13, 2022 via iPhone
    @afewok 恭喜你,猜对了,可惜没有奖励
    Bromine0x23
        140
    Bromine0x23  
       Apr 13, 2022
    个人倾向成功使用 2xx ;失败用 4xx 、5xx ,以 application/problem+json 格式表示异常信息
    Vitta
        141
    Vitta  
       Apr 13, 2022
    一把梭的你们是不是很多都是 java 开发
    yaott2020
        142
    yaott2020  
       Apr 13, 2022
    我自己写代码的话,除了常用的错误就会用,比如 401 500 503 403 404 。如果是应用层级的错误一律 200 OK ,然后再在返回包里面标注错误码,错误信息。
    bobo2
        143
    bobo2  
       Apr 13, 2022
    @Chad0000 正常来说,不都是 err.log 存放错误日志,out.log 存放接口输出日志么,都返回 200 ,不就都存 out.log 了,日志层再怎么实现也是要判断输出日志和错误日志的吧
    neutrino
        144
    neutrino  
       Apr 13, 2022 via Android
    @Vitta 语言无关,一把梭业务逻辑更清晰,前后端更舒服,何乐不为。
    binux
        145
    binux  
       Apr 13, 2022
    @lmmlwen 那么多公司规范都给出来了,还有没有人用,你真是坐井观天。
    adoal
        146
    adoal  
       Apr 13, 2022
    如果发生错误,是与业务逻辑无关的系统错误(不论是客户端还是服务器,自己方还是外方),还是与系统无关的业务错误?前者表明系统没毛病,是用户操作的错,要由用户解决,并且允许出现的常态;后者表明用户操作没毛病,是系统的错,由技术人员(某方的开发或运维)来解决,即使发生的频率不低也不应该视为允许出现的常态。有些错误很容易分辨出属于哪一类,而有些就需要技术和业务都精通的老司机才能准确分辨。HTTP 状态码和 body 数据之争的本质是,针对这两类错误如何设计报错机制。
    aragakiyuii
        147
    aragakiyuii  
       Apr 13, 2022 via iPhone
    infra 都没有的就随便一把梭了,毕竟能用就行
    txydhr
        148
    txydhr  
       Apr 13, 2022
    能 200 梭哈
    意味着也可以 404 梭哈
    反正都能打开网页
    MrKrabs
        149
    MrKrabs  
       Apr 13, 2022
    http 状态码才几个啊,restful 也就是个野鸡标准罢了,又不是圣旨
    a132811
        150
    a132811  
       Apr 13, 2022
    就算是完全用 200 一把唆, 也得判断 http code 。

    如果返回结果确实需要自定义 code ,就需要两层处理:

    // 第一层 无论如何都是要处理的
    if(res.statusCode==200) {
    // 第二层再判断一下 code
    if(res.json().code==0){
    // 正常业务代码
    )else if(res.json().code==1){
    raise Error(res.json().err)
    }
    }else if(res.statusCode==400){
    raise Error(res.body)
    }else if(res.statusCode==401){
    // 未登录.....
    }else if(res.statusCode==404){
    // .....
    }....500 ,501 xx
    --------------------------------------------------------------------------------
    如果返回结果简单,就用一层就够了。
    (很多公司的 api 其实就一层: https://v2ex.com/t/846785

    // 只有第一层
    if(res.statusCode==200) {
    // 只要是 200 , 就是正确的结果,不需要加第二层 code 判断
    // 正常业务代码
    }else if(res.statusCode==400){
    raise Error(res.body)
    }else if(res.statusCode==401){
    // 未登录.....
    }else if(res.statusCode==404){
    // .....
    }....500 ,501 xx
    ------------------
    jinliming2
        151
    jinliming2  
       Apr 14, 2022   ❤️ 4
    HTTP 2xx: 正常结果,一定是成功的操作,仅包含业务数据,不包含其他内容 status: success 之类的
    HTTP 4xx: 客户相关错误,状态码可以提供大范围的错误信息分类(未登录、无权限、访问频繁之类的),这些错误都是正常情况下的错误,可以给出具体理由的,属于预期错误,通常理论上完全不需要开发、运维介入的错误,用户自己知道错哪了,能不能解决也是用户自己可以判断的。
    HTTP 5xx: 服务相关错误,可以通过状态码进行分类分为业务错误和网关错误。如果是业务自己的错误(比如数据库连接断了、空指针了之类的),那就是 500 ,这类错误通常是需要开发人员去定位的异常情况,也就是未知的 bug 。而网关错误通常是明确的,配置错误 502 、临时维护 503 、业务挂了 504 。
    其中 4xx 和 5xx 错误,状态码只提供大分类,响应结构中再包含可公开的具体的细分错误详情。

    举个例子:用户访问了一个不存在的 URL ,分为 3 种可能,1 业务错误:没有这个资源,2 业务错误:没有权限,同时需要防止用户了解资源的存在性,3 网关错误:找不到对应的业务。
    对于 1 和 2 来说,返回 HTTP 404 再合适不过,HTTP body 指示找不到可访问的资源;而对于 3 来说,应该返回 502 ,HTTP body 指示找不到可用服务。

    这样,在网关记日志的时候就不需要解析 HTTP body 了,通过状态码就能过滤出错误日志,并且按照大分类分好:
    通常开发只需要关注 500 的业务错误、运维关注除了 500 以外的其他 5xx 错误。而 4xx 的错误通常只需要关注计数就行,错误数在一定量级以下就是正常,超过一定量级就需要报警让开发介入调查了。2xx 的日志只在查业务逻辑的时候才会关注,平时直接忽略。
    seakingii
        152
    seakingii  
       Apr 14, 2022
    @afewok 不用盲猜,上面已经有人说了.
    ktqFDx9m2Bvfq3y4
        153
    ktqFDx9m2Bvfq3y4  
       Apr 14, 2022 via iPhone
    @bobo2
    这都什么年代了日志任然需要依赖纯文件?结构化日志中间件了解一下: https://datalust.co/seq
    mmmfj
        154
    mmmfj  
       Apr 14, 2022
    有时候前端是允许一些异常情况的,但是你返回个 http 异常,太难区分了,不知道是真的服务器炸了还是特殊情况
    summerLast
        155
    summerLast  
       Apr 14, 2022
    自己定义格式有什么好处吗?
    nkidgm
        156
    nkidgm  
       Apr 14, 2022
    以前还会争论一下,现在这两个其实都有存在的理由,对于公司项目跟着团队 leader 走就行了,对于自己的项目用哪个看自己习惯。
    nyakoy
        157
    nyakoy  
       Apr 14, 2022
    以前是 200+post 一把梭,现在在往 restful 走。毕竟有规范还是用规范吧
    yc8332
        158
    yc8332  
       Apr 14, 2022
    感觉搞偏了。反正我们都是 code 是在返回数据里面。。http code 是代表请求的状态吧?我们从来不去改这个东西。
    newmlp
        159
    newmlp  
       Apr 14, 2022
    当然 200 一把梭,业务层的错误为啥要用传输层的错误码表示?
    sunny1688
        160
    sunny1688  
       Apr 14, 2022
    我这边的做法是,200 状态码,业务数据一定有,而且前端那边不需要做任何判断,拿到数据直接处理即可,就拿 ajax 来说,非 2xx 状态码,走的是 error 回调,很多开源请求库也都是按照这种方式去处理,所以我这边是只用了 200+4xx ,5xx 就不用说了,一般也不会手动抛出 5xx 状态码吧!
    Vitta
        161
    Vitta  
       Apr 14, 2022
    @neutrino #144 这就跟代码写抽象和 纯 CV 差不多了,纯 CV 又不是不能跑
    e7
        162
    e7  
       Apr 14, 2022
    telnet 127.0.0.1 8080
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    agsdfadf
    HTTP/1.1 400 Bad Request
    Connection: close

    Connection closed by foreign host.

    协议是协议,业务是业务,日志、监控啥的是没 es 还是没 prometheus
    eijnix
        163
    eijnix  
       Apr 14, 2022
    @daimubai 我们团队也是这种做法
    bfdh
        164
    bfdh  
       Apr 14, 2022
    200 一把梭,原因很简单,一些前端 /APP 端菜鸟根本不知道什么是 http 状态码。
    night98
        165
    night98  
       Apr 14, 2022
    你是 leader ,直接推
    不是 leader ,保持现状就行
    菜鸡哪都有,打开各个大厂的页面你会发现各种奇奇怪怪的 http 用法,大厂里面也存在两级分化,这事没办法
    l00t
        166
    l00t  
       Apr 14, 2022
    监控不了是监控的问题,不要削足适履。
    yamedie
        167
    yamedie  
       Apr 14, 2022
    路过围观一下,另外下次别刷前端娱乐圈了好吗?
    wy315700
        168
    wy315700  
       Apr 14, 2022   ❤️ 1
    一般 HTTP 状态码用于通信状态

    具体业务状态封装在 body 里
    lff0305
        169
    lff0305  
       Apr 14, 2022
    不知道现在如何了, 十多年前某银行的防火墙 /F5 还是什么会自作主张的修改非 200 的 response, 变成类似

    upstream: error 400
    messge: .......
    adoal
        170
    adoal  
       Apr 14, 2022
    另外,HTTP API 给 web 前端浏览器里用 JS 调用和给第三方应用系统的后端调用也是两种不同的场景,对最佳实践的选择也有影响。
    fromzero
        171
    fromzero  
       Apr 14, 2022
    http 层的 code 符合标准就行 。json 里面的 code 是业务相关的状态,随便怎么定义都行。
    hejw19970413
        172
    hejw19970413  
       Apr 14, 2022
    两个字 就够了 规定!
    AoEiuV020CN
        173
    AoEiuV020CN  
       Apr 14, 2022
    不管有什么优点有什么理由,修改这个都是麻烦,没好处,
    cozof
        174
    cozof  
       Apr 14, 2022 via iPhone
    应用层永远不应该去修改 http code
    hlayk
        175
    hlayk  
       Apr 15, 2022
    https://www.v2ex.com/t/808610#reply44
    有一点楼上没提到过的 在 Android 这边若使用网络请求框架 retrofit (业内排第一的网络框架)对于非 200 的 http code 是直接抛异常的 所以若返回非 200 的 http code 我们需要 try 然后再去解析 这非常的不优雅
    chanchan
        176
    chanchan  
       Apr 15, 2022
    有些人跳脱出技术讨论,直接就认定自己是对的,别人是错的,说什么别人就是懒、摆烂、什么"封闭文化"之类的。真让人恶心
    1  2  
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1163 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 203ms · UTC 18:25 · PVG 02:25 · LAX 11:25 · JFK 14:25
    ♥ Do have faith in what you're doing.