V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Muninn
V2EX  ›  编程

RESTful API 中的 Status code 是否要遵守规范

  •  
  •   Muninn · 2019-02-22 09:58:06 +08:00 · 7230 次点击
    这是一个创建于 2130 天前的主题,其中的信息可能已经有所发展或是发生改变。

    缘起

    事情是这样的,我在知乎受到邀请回答一个问题,主要是问 ID 找不到到底要不要用 Status 404。我回答的还是比较早的,那时候只有一两个回答。我本来以为这是没啥争议的,在一个学术的地方讨论学术问题,当然是要遵守规范了,结果过了几个小时大跌眼镜。自造 code 党竟然支持率第一,还好平时见的也很多的全 200 党没有受到支持,不然真的吐血了。

    为什么要遵守规范

    一般那种说特殊情况特殊处理,不要拘泥于规范的人,大多都是自己没搞清楚某些知识,拿这句话当作偷懒的借口。其实一般做项目没那么多特殊情况。

    为了更好的适应各种库

    大部分完善的 HTTP 请求库,都会依照 RFC 的规范去设计错误处理的流程,虽然处理方式各有不同,但一定会在文档说明错误处理的部分的。使用 RFC 标准能最大限度的兼容各种 HTTP 客户端。你说现在你用的 HTTP 客户端不处理 Status Code,但是你没法保证将来不重构,重构的时候还是不处理。

    一般调用 api 使用 js 或者 python 的概率比较大,我们看看知名的库。在 js 里,最近比较流行的 axios 默认会把 200 系列外的 code 归到异常里。在 python 里,最流行的 http client 是 requests,它更为详尽的预处理了 status code。

    为了开发者更好上手

    另外在管理团队的方面,我们的原则是尽量的减少一个项目的“规范”,这样才能更容易去遵守。能用标准的地方,一定不要自己定一个更复杂的规则。无论是服务端的维护者还是 API 的消费者是会换人流动的,每个进入项目的人熟悉一大堆无谓的自定义项目规范都要成本。

    更简单的办法是参考大厂

    其实给项目定规范,最不靠谱的是自己拍脑袋,稍好一点的是去知乎或论坛问,更好一点的是去 google 搜,最简单的是直接去看大厂的产品或者规范啊。API 本来就是个公开暴露的东西,还有比这更好找参考的吗?我们来看看:

    • Google 遵守规范
    • Github 遵守规范
    • Microsoft 遵守规范 顺便说一句,微软的 API 规范真的很具有指导意义。
    • Twitter 遵守规范
    • 阿里云 遵守规范
    • 腾讯云 不遵守规范 全部 200 事实上腾讯的技术比较混乱,每个项目都不一样。但最新要执行的统一规范是全部 返回 200 用返回值中的错误码表明错误。
    • 百度云 遵守规范

    我的建议

    很多人也许用着很简陋的 Web 框架,导致误以为返回了错误码,就不能返回 Response Body 了。其实你返回 204 外的任何 Status Code,最好都伴随着返回 Body。

    在项目规范里,可以规定 Status Code 遵照 RFC 标准,或者选定一个集合出来,把一些不常用的去掉。然后如果不是 200 系列的代码,必须伴随着这样的一个错误结构:

    {
        "error": "UserNotFound",
        "message": "该用户没有找到"
     }
    

    这样错误分为了三层结构,第一层是 Status Code,使用者能大概知道是什么问题。第二层 Error 是一个 Key 使用约定好的无空格的英文,给使用者做判断用,使用者可以根据 Key 自定义接下来的操作。第三层是 message,有些 Key 使用者可以决定直接把 Message 显示个终端客户。

    如果是微服务项目,需要要求每个服务不管用什么语言,都要把错误统一成这个样子。如果开发者告诉你框架不支持,那这一定不是个好框架,改重构了。好的框架不仅能让你自定义错误内容,还能做到所谓的“框架自己出错的返回”也由你自定义。比如路由没有找到之类的。

    最后

    我实在不明白为什么一个最扯淡的答案,要自造一个 600 的 status code,可以得票第一。知乎用户到底有没有一点独立的判断精神啊,只要装的一本正经,再摆出来一点资历,哪怕是胡说八道,大家也纷纷点赞。也许真的不适合在知乎去回答技术问题了。

    第 1 条附言  ·  2019-02-22 13:06:18 +08:00

    另外说明一点,别人问我code用什么,我回答一般是看你们自己的内部规范,没规定就按照RFC标准来。
    别人问我code的内部规范该怎么定,我建议一般是按照RFC标准来,缩短规范长度。

    我反对的是:宣扬不该遵守规范这件事
    而不是:不遵守规范的人,只要不影响别人,个人爱怎么用怎么用,我管的着吗?

    38 条回复    2019-02-23 21:42:12 +08:00
    WilliamYang
        1
    WilliamYang  
       2019-02-22 10:36:06 +08:00   ❤️ 1
    支持你遵守规范, 为你点赞
    casillasyi
        2
    casillasyi  
       2019-02-22 10:38:24 +08:00   ❤️ 2
    不遵守规范,就是给后人埋坑
    huangdayu
        3
    huangdayu  
       2019-02-22 10:53:57 +08:00 via Android   ❤️ 1
    我是用 status code,然后内部也定义了 msg 对应的 code
    momocraft
        4
    momocraft  
       2019-02-22 10:56:00 +08:00   ❤️ 1
    故意不遵守的不必硬要叫 rest
    relaxgo
        5
    relaxgo  
       2019-02-22 11:10:29 +08:00 via Android   ❤️ 2
    计算机行业最可怕的人,就是这种不遵守规范,自行其是的人了。居然还自行定义个 600,如果之后规范里增加了 600,岂不是冲突了
    feiyuanqiu
        6
    feiyuanqiu  
       2019-02-22 11:12:56 +08:00   ❤️ 1
    restful 这东西没什么技术含量,谁都可以抒发一下个人见解,导致争论不休。到最后就变成个人品味的争执了,谁都说服不了谁。有时间有闲心的话可以去吵一吵,我先亮一下我的屁股,我是 paypal 规范的忠实支持者:

    https://github.com/paypal/api-standards/blob/master/api-style-guide.md#http-status-codes
    relaxgo
        7
    relaxgo  
       2019-02-22 11:20:52 +08:00
    这种不遵守规范,还污染规范的,更是让人气愤
    pubby
        8
    pubby  
       2019-02-22 11:26:44 +08:00 via Android
    个人比较讨厌 restful
    全部 post,全部 200
    mcfog
        9
    mcfog  
       2019-02-22 11:55:38 +08:00
    首先 restful 根本不是规范,而是设计风格
    其次,你参考的大厂 API 是第三方整合用的 API,而很多人讨论的背景是自己公司做给自己人的场景
    restful 是学术(或者说学院派)没错,但你怎么就觉得知乎是学术的?这个问题是学术的?我怎么看怎么觉得这个问题是工程角度的

    工程问题并不是学术问题,不是是非黑白问题,而是权衡和折中的问题,我真的从没见过哪家的设计是 100%按照全套 restful / rfc 的东西来实现的,patch 方法 + `application/json-patch+json` 的修改接口看都没看到过,HATEOAS 的东西最多在分页里见过一点
    Muninn
        10
    Muninn  
    OP
       2019-02-22 12:06:51 +08:00
    @mcfog RESTful 是基于 HTTP 通讯协议的一种数据交互风格,

    我们讨论的是要不要遵守 `HTTP 协议规范` ,不是 RESTful 的规范。你连这个省略都看不懂,后边还说了这么多……
    Muninn
        11
    Muninn  
    OP
       2019-02-22 12:08:40 +08:00
    @WilliamYang
    @casillasyi
    @huangdayu
    @relaxgo

    感谢各位的支持,看来 V2 还是倾向于尽量遵守规范的比较多。
    mcfog
        12
    mcfog  
       2019-02-22 12:43:20 +08:00
    @Muninn HTTP 协议更不是规范了,你说用 600 状态码违反 HTTP 协议那是对的,用 200+body 内容表示业务状态完全符合 HTTP 协议,不符合 restful 风格而已

    你觉得我不懂的话那就这样吧,祝你工作顺利
    MeteorCat
        13
    MeteorCat  
       2019-02-22 12:45:27 +08:00 via Android
    返回 200 状态,body 里面加个 status 就行了,HTTP 状态真的没必要搞什么幺蛾子
    HelloAmadeus
        14
    HelloAmadeus  
       2019-02-22 12:47:33 +08:00 via iPhone
    Failures MUST be reported in the 4xx or 5xx range. This is true for both system errors and application errors.同意这个观点
    micean
        15
    micean  
       2019-02-22 12:50:01 +08:00
    看机房环境
    有的甲方机房管理的莫名其妙,防火墙瞎 JB 配
    icylogic
        16
    icylogic  
       2019-02-22 13:28:48 +08:00 via iPhone
    我网站关了以后就给人返回 418 ……
    justfindu
        17
    justfindu  
       2019-02-22 13:55:03 +08:00
    应该是支持 http status code 吧. 毕竟程序内其他非 2xx 都是抛异常, 然后统一处理. 方便中断. 一般异常再加上自有 error code.
    hilbertz
        18
    hilbertz  
       2019-02-22 14:00:50 +08:00
    restful 设计局限性太大,正常程序员思路都是用 rpc
    tongz
        19
    tongz  
       2019-02-22 14:05:16 +08:00
    @icylogic #16 我网站关了以后直接在 nginx 里返回 http code 666, 哈哈.
    passerbytiny
        20
    passerbytiny  
       2019-02-22 14:11:44 +08:00
    除了“ ID 找不到用不用 404 ”,其它的都认同。“使用 ID 未找到资源”并不能完全等同于“资源不存在”。如果严格考虑上下文的话,直接用一个 ID 形式的 URL 去获取资源但是没找到(场景一)是“资源不存在”,根据前置检索的结果拼接一个 ID 形式 URL 去获取资源但是没找到(场景二)是“未知原因引起资源已不可用”,场景一是 404,场景二应该是 500,场景一如果考虑资源被禁用、资源被删除的情况,是不是 404 也不好说。
    “使用 ID 未找到资源”这个应该还是要根据不同情况不同分析,面向公众的开放接口应当返回 404 + error body,面向信息管理系统前端界面的后端接口返回 500 + error body 就更合适,而如果是纯 HTML 资源获取的话,返回 200 + 提示界面才是更好的选择。
    est
        21
    est  
       2019-02-22 14:26:04 +08:00   ❤️ 1
    RESTful 过时了 REST-like 刚刚好。
    loading
        22
    loading  
       2019-02-22 14:27:34 +08:00 via Android
    @est +1,落后的规范基本覆盖不了所有情况。
    Muninn
        23
    Muninn  
    OP
       2019-02-22 14:41:20 +08:00
    @passerbytiny 同意你说的前半部分,ID 找不到的确有很多种可能。 这块因为因为这篇文章的知乎问题描述的很清楚,数据库里没有这条数据。所以这种情况下应该是 404 的。

    否则根据情况可能是 400 401 403 404 410 都有可能。

    但是你对于 500 的认识或许可以再去研究一下。500 的含义是表明这是一个未曾预料的问题。只有程序出 bug 了异常没有被处理,或者处理过程未预料的中断比如读数据库出错了的这些情况才会出现的。一个好的服务是不应该出现 500 的,出现了就可以肯定是服务方的错:)

    场景二你描述的其实比较模糊,有可能是你觉的如果 id 是你提供的,客户端的请求肯定是对的,所以在程序里觉得如果找不到肯定是出现了未预料的状况,好了我认怂抛出个 500,要是出现了前端会找我,我再查查 bug。
    但其实不存在这种状况的,因为 RESTful 你是不可控客户端的呀,说不定是前端同学拼 id 拼错了呢,调试的时候一看 500,锅直接就抛给你了。。。
    mooncakejs
        24
    mooncakejs  
       2019-02-22 15:04:57 +08:00
    “ ID 找不到用不用 404 ”, 平时也是返回 404,但是每次回想起来都很别扭,实际上从主观出发还是支持找个 4xx 的状态码比较好。
    rayhy
        25
    rayhy  
       2019-02-22 16:21:53 +08:00
    我个人也是按照楼主说的这样做的,但写项目的时候还是会遇到一些 http status code 不够用或者不够准确的情况,不知道楼主针对这些情况是怎么处理的?除了 http status code 之外,再增加一个 error code?
    Muninn
        26
    Muninn  
    OP
       2019-02-22 16:53:43 +08:00
    @rayhy 看“我的建议” 那一节。

    只要是错误,一定要自定义一个统一的错误结构体。可以返回第二级的 error code (可以是数字或者字符串),和 message。

    我贴的微软的链接和下边有人贴的 paypal 的链接,也可以参考。不过有些过于复杂了,属于特别理想化的错误返回。
    我和那些理想化的规范的核心不同是,我只考虑返回单个错误,而他们考虑了返回多个错误的情况。

    多个错误又有链式的过程中出现的错误和列表处理中出现的并列错误两种情况。

    实际上太复杂了为写程序造成负担,还是只返回一个最直接的错误好操作一些。
    sparkle2015
        27
    sparkle2015  
       2019-02-22 18:17:34 +08:00
    @pubby

    > 个人比较讨厌 restful
    > 全部 post,全部 200

    -----

    那你可以尝试一下 GraphQL
    AlisaDestiny
        28
    AlisaDestiny  
       2019-02-22 19:56:24 +08:00
    资源未找到用 404 的话会有歧义,到底是资源不存在还是 URL 不存在?
    同意#6 说的 PayPal 规范:
    Range Meaning
    2xx Successful execution. It is possible for a method execution to succeed in several ways. This status code specifies which way it succeeded.
    4xx Usually these are problems with the request, the data in the request, invalid authentication or authorization, etc. In most cases the client can modify their request and resubmit.
    5xx Server error: The server was not able to execute the method due to site outage or software defect. 5xx range status codes SHOULD NOT be utilized for validation or logical error handling.
    akira
        29
    akira  
       2019-02-22 20:55:43 +08:00
    公司内部只要约定好 用不用 404 都可以吧
    ydirel
        30
    ydirel  
       2019-02-22 22:49:26 +08:00
    要遵守规范,status code 都有标准规定含义的。不然就会和标准冲突。
    dengshen
        31
    dengshen  
       2019-02-22 22:55:40 +08:00 via iPhone
    神烦后台某个字段为空时连字段都不返回
    例如正常数据
    {name:xxx ",id:1}
    实际数据
    {id:1}

    美其名曰:节约带宽!
    johnniang
        32
    johnniang  
       2019-02-22 23:32:55 +08:00 via Android
    @AlisaDestiny 这个问题很好解决。

    如果是 URL 不存在,将只会得到 status = 404 ;

    若是资源未找到,不仅仅是 status = 404,而且还会返回一个错误 body,其中 errorcode = 404。

    方能区分之。
    Muninn
        33
    Muninn  
    OP
       2019-02-23 09:37:11 +08:00
    @AlisaDestiny 可以看看我帖子中 “我的建议”章节。

    然后 paypal 和 microsoft 之类的 API 规范里也有类似的处理方案。
    xfriday
        34
    xfriday  
       2019-02-23 12:11:07 +08:00
    可以在 http header 里添加自定义字段比如 My-Status: 1004,这样可以避免和常规状态码冲突或者不够用的情况
    xfriday
        35
    xfriday  
       2019-02-23 12:14:09 +08:00
    在 http response body 里定义自定义状态(json/xml),无法处理字节数据,比如图片,到时候还是要退回到 http status code 来做业务判断
    vibbow
        36
    vibbow  
       2019-02-23 16:02:38 +08:00   ❤️ 2
    Muninn
        37
    Muninn  
    OP
       2019-02-23 21:26:41 +08:00
    @vibbow 哈哈这是怨念有多深
    Muninn
        38
    Muninn  
    OP
       2019-02-23 21:42:12 +08:00
    @xfriday 额外信息放在 Header 中也是个流派,但是这不是 Resp Body 的替代品。

    首先,一般开发中图片都用的对象存储+CDN。

    好,我们来考虑自己生成非文本对象的情况。

    其实在 Response Body 中放 Error 结构体本质上就是正确返回和错误返回是不同的结构体,在客户端也会进入不同的处理流程。所以这跟正常结果是 JSON 还是 Object 没关系,错误了始终是一个错误结构体。

    反而是全 200 流,一般才把错误放在 Resp body 中,导致如果正常返回不是一个 JSON 就没办法做了……
    你又找到了一个全 200 流的缺陷。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2673 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 08:31 · PVG 16:31 · LAX 00:31 · JFK 03:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.