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

API 使用 HTTP 状态码还是全部返回 200

  •  
  •   schemacs · 2015-05-16 15:43:29 +08:00 · 28340 次点击
    这是一个创建于 3230 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,方案一:所有就是只以HTTP状态码来表示状态,200时候返回内容就是数据,最多有个分页(其实分页我都不想放响应内容里,我觉得像github那样放到header更整洁)。

    {
        "count": 1,
        "next": null,
        "previous": null,
        "results": [
            {
                "id": 1,
                ...
            }
        ]
    }
    

    只有在抛出Exception异常(即使业务逻辑上有问题,也抛出APIException异常)才返回:

    HTTP/1.1 405 Method Not Allowed
    Content-Type: application/json
    
    {"status_code": 405, "message": "Method 'DELETE' not allowed."}
    

    方案二:所有接口都返回200(我觉得有点不切实际,比如403,500等,再加上很多客户端http库都会对这些抛异常的),然后响应里包含:

    {
        "status_code: 1000
        "message": "xxxxx"
        "result": {
            "id": 1
            ...
        }
    }
    

    现在貌似很多客户端开发都希望是方案二,因为这样他们逻辑会简单些(200以外全去捕获异常,200时再看status_code做不同处理),但是作为服务端更倾向于方案一,因为大多框架和各家提供的API都是这么干的,其实也很简单(先看状态码,然后直接根据状态码决定后续动作)。

    在两个方案中纠结,来请大家给点建议哈~

    以前都是在SO上发帖,第一次在v站发帖,如有没说清楚或者违反社区原则的地方,请大家不吝指出。

    65 条回复    2015-05-21 21:45:37 +08:00
    Comdex
        2
    Comdex  
       2015-05-16 16:08:13 +08:00
    我也是用方案二,所有请求都返回200,然后在响应里有个status code表明服务请求状态,要不感觉响应里有status code和http 也有状态码是不是多余了?
    invite
        3
    invite  
       2015-05-16 16:55:48 +08:00
    但是作为服务端更倾向于方案一,因为大多框架和各家提供的API都是这么干的,其实也很简单(先看状态码,然后直接根据状态码决定后续动作)。

    这个结论怎么得出来的?
    kslr
        4
    kslr  
       2015-05-16 16:55:59 +08:00   ❤️ 1
    有些地方直接抛非200状态,JS就傻眼了
    invite
        5
    invite  
       2015-05-16 16:57:20 +08:00
    补充一点,用HTTP状态码判断,这个方案让人想起了之前那个 @CloudXNS 的API,居然还得用不同的HTTP方法,神一般的设计。
    pubby
        6
    pubby  
       2015-05-16 17:07:22 +08:00 via Android
    如果不走HTTPS,会不会有些地方奇葩运营商对一些HTTP状态码进行一些自定义行为
    schemacs
        7
    schemacs  
    OP
       2015-05-16 17:24:39 +08:00
    @Septembers 多谢,我们现在可能主要不是Web的javascript,而是针对客户端,不过可以按照同样方法处理。200和204之间我们会遵循RESTful惯例。
    @Comdex 这个只是方便了客户端而已,一个是http rfc里定义的状态,一个是业务逻辑定义的状态。方案二十忽略了前者。
    @invite 框架比如DRF,http://www.django-rest-framework.org/api-guide/exceptions/ ,‘各家’以github为例: https://developer.github.com/v3/#client-errors,你看她其实是没有自己定义业务状态码(服用http已有的),只是定义了出错下不同的message。
    @kslr js也可以success,fail啊?
    @invite 能给个具体链接吗?不同方法不是本地讨论范围内,那个是RESTful的惯例。
    @pubby 这个的确是个问题,多谢提醒,后续如果真出现这样的问题,我们会部署的HTTPS。

    另外一点从讨论中开始浮出来了:就是是沿用HTTP规范的状态码(但是自己提供message),还是不用HTTP那套(所有的都用200),自己定一套status code+message?
    invite
        8
    invite  
       2015-05-16 17:37:13 +08:00
    @schemacs 果然,你说到框架了,哈哈。框架不多说了,每个人有自己的喜好。至于github没有业务状态,那请问如果错误类型较多,怎么办?光靠他返回的英文内容?那国际化问题怎么办?还得配合Google翻译啊?
    GhostFlying
        9
    GhostFlying  
       2015-05-16 17:49:42 +08:00 via Android
    @invite 这需要什么国际化,本质上返回的错误信息还是要客户端自己处理掉的,也不是直接返回给用户
    schemacs
        10
    schemacs  
    OP
       2015-05-16 17:49:54 +08:00
    @invite 错误类型较多?一个HTTP的API如果错误类型超过了HTTP规范里的状态类型,我觉得这个API需要重构了。还有客户端对大部分服务端错误,除了类似临时出错,重试就能解决那些,其他的基本只能友好地提示下用户。github目前没有应该是自定义的错误状态列表,目前我们的逻辑也还还没超过github~ github目前没有做国际化,即使后续国际化,只要服务端检测请求的语言(或者加locale参数),返回对应语言的文本,这个应该也不成问题。
    giuem
        11
    giuem  
       2015-05-16 17:52:44 +08:00 via Android
    有些ISP会对非200的状态码返回特殊内容,比如一些广告
    schemacs
        12
    schemacs  
    OP
       2015-05-16 18:03:57 +08:00
    @giuem 嗯,@pubby已经提醒了我,后续会部署HTTPS。
    jokester
        13
    jokester  
       2015-05-16 18:18:43 +08:00
    HTTP那个是协议层的状态
    API可以有符合自己语义的状态
    zeayes
        14
    zeayes  
       2015-05-16 18:29:31 +08:00
    @jokester 说的好。

    如果使用方案一,前端就不能很好的处理某些较为复杂的业务逻辑了。比如登录接口,到底是”账号不存在“,还是”密码错误“,后端需要根据业务返回不同的错误码,然后前端根据错误码进行对应的业务处理,”账号不存在“,可以跳转到注册页面,“密码错误”可以清空密码,让用户重新输入。
    learnshare
        15
    learnshare  
       2015-05-16 18:36:31 +08:00
    一律使用统一的状态码,无需讨论。
    mytharcher
        16
    mytharcher  
       2015-05-16 19:11:48 +08:00   ❤️ 7
    HTTP RESTful API 就是针对资源的,协议的状态码也就是资源响应的状态码,其实直接用足够了。如果觉得不够,可以加自定义的小数点进行细分,比如 400.1 代表输入数据格式不匹配,400.2 代表另一个输入数据问题等。

    服务端状态的文本消息是没有必要放在响应里传回来的,都应该是前端根据码表对应处理。所以基本不存在国际化的问题。

    具体到“登录接口”的“账号不存在”或“密码错误”这种情况,就是对于 session 这个资源创建新的会话没有权限,后端可以用 401 或者 403 来返回状态,而前端拿到状态怎么处理那就随便了。

    而前些年大家都用 200 的后端返回,而在数据里再用 `status` 来表示状态,一方面是 RESTful 的理念还没有普及,另一方面也是很多框架不支持,自己实现比较麻烦。现在如果希望设计成 RESTful 的 API,就可以直接使用 HTTP 协议里的很多特性,而没必要再自己定一套规则。
    schemacs
        17
    schemacs  
    OP
       2015-05-16 19:12:30 +08:00
    @jokester 是的,但是现在客户端开发倾向于不处理前者,指处理后者。
    @zeayes 这种复杂情况的确是,我现在能想到就是这个时候再加上具体错误码 {"code": xxx, "message": "username or password is incorret."},而平时没有异常时返回中不额外加code和message(那时候也基本没用),而出错逻辑我们(全局)可以控制,比如自定义exception。
    @learnshare 方案一 or 方案二?
    schemacs
        18
    schemacs  
    OP
       2015-05-16 19:13:37 +08:00
    @mytharcher 你说的最后一段,我深表赞同。
    jokester
        19
    jokester  
       2015-05-16 19:21:51 +08:00
    @mytharcher 有道理
    方案2这样在HTTP之上再造协议的方式, 更接近以前的RPC
    learnshare
        20
    learnshare  
       2015-05-16 20:11:03 +08:00
    @schemacs
    like @mytharcher said,使用 统一的 HTTP 状态码 https://tools.ietf.org/html/rfc2616#section-10
    话说大家都不知道协议里规定的状态码?
    lincanbin
        21
    lincanbin  
       2015-05-16 20:24:14 +08:00
    统一状态码,要开发者在状态码上做鉴定是一件非常麻烦的事。
    jimrok
        22
    jimrok  
       2015-05-16 20:59:43 +08:00
    服务器端不一定真能保证200的状态码,除非你去修改web服务器。500,502直接会让你的客户端崩溃。所以,严格按照方案1是比较靠谱的,客户端只需要判断三种情况,2开头的是正常的,4开头的,是客户端的错误,5开头的是服务器端出错。
    schemacs
        23
    schemacs  
    OP
       2015-05-16 21:00:12 +08:00
    @learnshare 是的,很多客户端开发都不清楚有这么多可以用啊。当然现实中一个接口不会用太多。
    schemacs
        24
    schemacs  
    OP
       2015-05-16 21:02:08 +08:00
    @jimrok 所言极是,标准里就已经分类好了的,但是客户端同学好像不喜欢这么做,他们习惯了方案二。。。
    invite
        25
    invite  
       2015-05-16 22:01:29 +08:00   ❤️ 1
    @schemacs 如果你硬要这么说,那也没办法了。等那天觉得状态码不够用了,你就自己在协议上扩展吧,杂交一下未必不可。

    不过这里还是想说一下,接口逻辑与HTTP协议本身分开,是有利于接口脱离HTTP而存在的的,否则后续如果不走RESTful了,接口代码又得变了。因此,个人还是建议在接口中返回相应的数据,这样即使改成其他方式了,端到端的业务都不会有任何影响。
    schemacs
        26
    schemacs  
    OP
       2015-05-16 22:31:36 +08:00
    @invite 是的,如果不走RESTful,比如走thrift,这个方案就行不通。但是如果是thrift,就直接抛异常就好。
    FrankFang128
        27
    FrankFang128  
       2015-05-16 22:35:13 +08:00 via Android
    全都使用200就等于放弃返回码的语义,不推荐,虽然很多人都这样干,还是不推荐。
    Septembers
        28
    Septembers  
       2015-05-16 23:09:58 +08:00 via Android
    bdnet
        29
    bdnet  
       2015-05-17 00:36:53 +08:00
    全部用200,除非客户端不支持 header http code才会考虑,否则不建议在body里面自己定义一个code(这很明显是多余的,很多 rest client 默认支持 header http code。
    bdnet
        30
    bdnet  
       2015-05-17 00:38:55 +08:00
    schemacs
        31
    schemacs  
    OP
       2015-05-17 08:30:19 +08:00
    @Septembers 嗯,很久前看过,现在关键是客户端同学对RESTful啥概念都没有。。。
    @bdnet 是的,我也认为多余,但是一大堆人都倾向于方案二,自己造。。。
    Septembers
        32
    Septembers  
       2015-05-17 08:51:23 +08:00 via Android
    @schemacs 如果是这种情况 引入JSON-RPC会好些 封装好基本不会太在意接口细节 使用基本就和用本地方法差不多
    schemacs
        33
    schemacs  
    OP
       2015-05-17 08:52:41 +08:00
    看到dropbox的blog,我也坚决选择方案一了 https://blogs.dropbox.com/developers/2015/04/a-preview-of-the-new-dropbox-api-v2/

    We’ve also simplified our use of HTTP status codes for errors. For errors that are common to all API calls, we do the same thing as before: 400 for bad request, 401 for auth failure, 429 for rate limiting, etc. But if a request fails for some call-specific reason, v1 might have returned any of 403, 404, 406, 411, etc. API v2 will always return a 409 status code with a stable and documented error identifier in the body.
    way2exluren
        34
    way2exluren  
       2015-05-17 09:16:47 +08:00 via Android   ❤️ 1
    HTTP状态码是给HTTP协议用的,反应HTTP协议的情况。你用HTTP协议传消息,是在HTTP上又加了一层通信协议……所以应该自己控制返回消息……不应该用HTTP状态码。协议要分层。
    mengzhuo
        35
    mengzhuo  
       2015-05-17 10:57:20 +08:00
    其实还是要看你们的客户端同学的能力的
    我原来也是用statuscode的
    结果客户端一票人都跟我说实现不了……
    硬生生地把漂亮的代码改成💩一样了
    schemacs
        36
    schemacs  
    OP
       2015-05-17 11:03:41 +08:00
    @mengzhuo 哈哈,还是平时库多了。。。
    schemacs
        37
    schemacs  
    OP
       2015-05-17 11:05:31 +08:00
    @mengzhuo 库用多了,就不知道自己该怎么处理了。记得有次iOS某个库,如果出错了(4XX之类),连响应body都不给你,只给你header,当时纠结我好久。
    mengzhuo
        38
    mengzhuo  
       2015-05-17 11:11:01 +08:00   ❤️ 1
    @schemacs

    哎……说库不好的其实都是不看文档的

    我觉得💩一样的代码写得很不爽,co 了一份客户端的代码
    发现用的库的文档里面赫然写着onErrorCallback(statuscode, body),
    然后我看了看为什么他们说实现不了,结果发现他们都是把逻辑放onSuccessCallaback
    这事还出在客户端主程身上……

    立马怒了找他们老大,结果人家丢一句,新系统上了么,我说上了,他说算了吧……
    benjiam
        39
    benjiam  
       2015-05-17 11:14:45 +08:00 via Android   ❤️ 1
    工程角度看第二种好多了,第一种的移植性太差。从协议本身看,需要一层协议描述消息完整性,上面一层才是语义。所以HTTP 200并不好,更多是因为简单,事实标准。restful这套有道理,但是用XML soap可以完全取代restful这套,为什么SOAP输了,更多是因为复杂程度,所以没成为事实标准。只要成了标准,做什么都是对的。
    iyangyuan
        40
    iyangyuan  
       2015-05-17 11:31:55 +08:00 via iPhone
    怎么高兴怎么来
    learnshare
        41
    learnshare  
       2015-05-17 11:35:29 +08:00
    我想问支持 200 的这些朋友,你在家说方言,出门也说方言么?
    pysama
        42
    pysama  
       2015-05-17 11:35:45 +08:00
    有没有同学从老板的角度说说看法呢?
    两种方案对于产品的影响是什么?哪种方案的ROI更高?(毕竟技术是为产品服务嘛~~)

    我突然想到某位晋升很快的一个朋友说过“技术只是手段”,我觉得我们可以多从其他角度想想。
    或许再回过头来看,就会有种感悟——“不用在意那些细节”

    当然,我上面说的话,可能,对于大部分开发者来说,都是不中听的。
    我非常理解,那就请你们多包涵。

    不说了~~
    invite
        43
    invite  
       2015-05-17 11:59:17 +08:00
    @schemacs 你真应该好好读下 @way2exluren 的回复。

    你刚才引用的 "v2 will always return a 409 status code with a stable and documented error identifier in the body." 人家也是有错误ID返回的。 按你之前的意思,这是不是也算 “自己造” 了?
    jokester
        44
    jokester  
       2015-05-17 12:08:05 +08:00   ❤️ 1
    @pysama
    老板没做过, 从管理角度看恐怕方案2开发成本要低些
    RESTful那一套是对HTTP的横向扩展, 吃透需要开发者有不坏的sense
    方案2是在HTTP上层扩展, 没那么多要求

    从技术面来看
    Dropbox的服务是关于文件的, 他的API的语义天然地接近HTTP Resource, 不另外做扩展层有利于从HTTP的特性得益(如cache)
    但是如果我要做的API不需要cache呢? 如果我的API甚至不是关于某种资源呢? 强行REST的好处在哪?
    twitchgg
        45
    twitchgg  
       2015-05-17 14:29:15 +08:00
    http://events.linuxfoundation.org/sites/events/files/slides/Andrei_Shakirin_REST_CXF.pdf
    这里有个基于Apache CXF JAX-RS框架的restful api设计指南。感觉能帮到你
    schemacs
        46
    schemacs  
    OP
       2015-05-17 15:22:33 +08:00
    @mengzhuo 那个bug有人同问后,官方给出方法是自己重写方法去实现的。
    @learnshare 赞同,尽量用大家认同的,不够用了再自己搞私有的(业务)状态码。
    @invite 嗯,是的,出错时会有detail和code,但是这个情况只在HTTP状态码不能满足的情况下。
    @pysama 没有老板,只有技术哈
    @jokester 嗯,我们目前没打算用HTTP以外的,至少API层(其他数据,如日志,推送等另说)。
    @twitchgg 多谢,大致看了下,正常情况下也是纯数据,只有出错时才有status和message。
    zhicheng
        47
    zhicheng  
       2015-05-17 15:32:24 +08:00
    1,HTTP 是给使用 HTTP 的业务设计的,这也是为什么它是“应用层协议”。
    2,如果不能理解第一点,那么请在心里默念100遍,"设计HTTP协议的人比我聪明"。
    mthli
        48
    mthli  
       2015-05-17 16:33:36 +08:00 via Android
    用方案一吧。写个API库封装一下,这样客户端用的开心你们也开心。我以前尝试给Dribbble API封装库的时候就是选择方案一的。
    jarlyyn
        49
    jarlyyn  
       2015-05-17 16:42:41 +08:00
    这个话题快赶上php是最好的语言了。
    poke707
        50
    poke707  
       2015-05-17 18:07:33 +08:00 via Android
    status code 应该只用来表达通讯状态,业务状态你可以放在response header自定义一个X-XXX-code
    sunus
        51
    sunus  
       2015-05-17 19:31:06 +08:00
    http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

    这篇文章做了一些总结。理论上应该用状态码。实践上用envelope返回状态码的非常多。个人也不觉得有什么本质区别,更多是一种风格和约定。

    我觉得主要是json response没有一个普遍采用的标准。 有一些标准但没有被广泛采用 http://stackoverflow.com/questions/12806386/standard-json-api-response-format
    jokester
        52
    jokester  
       2015-05-17 19:41:04 +08:00
    @zhicheng
    按这个逻辑, 发明HTTP的人想必是觉得自己比发明TCP的聪明
    izhaohui
        53
    izhaohui  
       2015-05-17 19:53:58 +08:00 via Android
    说一下我的经历,从前一个项目的开发一直是用200状态加json 返回的形式做的,前端开发人员写ajax代码也都从来不写错误回调而是依赖全局的错误回调,有一次一个新模块实验性上restful,结果那几天前端像疯了一样抱怨你们的接口都不能用,各种4xx,5xx的错误,结果是我们在响应体封装了消息他们都不看,只说这样的不合理。感觉我们的接口质量很差,然后新模块就继续json + 200了。感觉也是一个接受程度的问题,在他们的观念里只要不是200一定是你后端问题,说明你接口不稳定。而不考虑是我们把错误信息传递到了另一个层面。
    schemacs
        54
    schemacs  
    OP
       2015-05-17 20:37:18 +08:00
    @sunus 第一个链接不错,我现在做法是只有出错时才带Envelope。JSONP是个问题,但是暂时用不到。
    miukal
        55
    miukal  
       2015-05-17 21:02:41 +08:00
    对于这个问题,首先两种方式肯定都可以实现业务的,而且实现难度并没有很大差别,维护难度可能也没有很大差别。restful 只是一种约定,一种选择,不一定在所有情况下都"更好"。
    对于开发者自身来说,这个约定目前比较流行,了解一下是应该的。
    但在实际工作的项目中做出选择,需要考虑自己的工作环境。如果项目相关开发人员都了解并认可这种约定,那么就使用,因为不会带来很多额外成本。如果处在一个项目相关开发人员不了解,也没有愿望去了解和接受这个新约定的环境下,使用这个约定可能就是给项目找麻烦了。
    elvba
        56
    elvba  
       2015-05-17 21:12:09 +08:00
    我觉得这就是理想和现实的差别,作为服务端,先假设所有服务端开发者都能完全理解并实现 RESTful API,但是对于前端开发者,APP 开发者来说,很多人都不知道 HTTP 协议,他们不需要也没必要了解 HTTP 协议的具体内容,只需要知道能请求某个地址,获取到结果就行了。所以具体实践的时候遭到反对也是必然的了。
    再说服务端,说自己的 API 是 RESTful 风格的,以及打算使用 RESTful 设计 API 的,或者正在这么实践的同学们,你们真的觉得自己实现了完整的 RESTful API 了么?这是个需要多次实践才能把握的东西,没有谁能保证一上手就能做好吧,和写代码一样,过段时间就会发现之前不好的地方,但是 API 又不能随便改,只能等下次大版本更新的时候来改了。
    最后,RESTful 只是一个风格而已,实际开发中还是怎么好用怎么来的好。
    elvba
        57
    elvba  
       2015-05-17 21:13:28 +08:00
    @miukal 赞同!
    refresh
        58
    refresh  
       2015-05-17 21:16:59 +08:00
    用http status code,客户端封装一个函数即可、
    odirus
        59
    odirus  
       2015-05-17 22:31:21 +08:00
    GET、POST都傻傻分不清的人大有人在,之前在组上提过用 RESTful 的方式来实现API,后来终于说服他们了,他们在代码中新建了一个名字叫做 REST 的模块。。。。这就是我们的RESTful API,现在新进来成员的时候我都要花费口舌来介绍为啥我们的框架里面有个叫做REST的模块。醉了。
    pi1ot
        60
    pi1ot  
       2015-05-17 22:46:30 +08:00
    @giuem 是的,非200返回码经常被ISP和浏览器拦截处理。
    pubby
        61
    pubby  
       2015-05-18 00:44:26 +08:00 via Android
    @jokester 同意,RESTful就像流行时尚,某年流行洞洞鞋,好了,本来人字拖好好的也要换个洞洞鞋穿
    anyforever
        62
    anyforever  
       2015-05-18 10:17:12 +08:00
    刚刚看到一篇相关的,推荐之。http://zhuanlan.zhihu.com/prattle/20034107
    invite
        63
    invite  
       2015-05-18 12:37:23 +08:00
    @schemacs 状态码不能满足的情况下,用错误代码,那不是得两套逻辑了?那何必搞那么复杂呢?
    ajianrelease
        64
    ajianrelease  
       2015-05-18 16:02:36 +08:00
    同意@invite,感觉方案二好
    benjiam
        65
    benjiam  
       2015-05-21 21:45:37 +08:00 via Android
    英语是最好的语言,所以 问好请说 是不是你很好。restful很好?挺傻的。有本事不依靠HTTP协议,自己独立的设计一套协议
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4964 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 09:48 · PVG 17:48 · LAX 02:48 · JFK 05:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.