V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
odirus
V2EX  ›  分享创造

[规范制定征求] 后端 API 接口返回值

  •  1
     
  •   odirus · 2019-10-30 15:12:29 +08:00 · 5598 次点击
    这是一个创建于 1840 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    我们公司正在快速发展状态,是时候开始制定各项技术标准规范(各种技术栈的语言规范)和工作流程从开发、测试到上线等)了,否则后期越来越玩不转。

    前期虽然有引进各种规范,但总是支离破碎,而且有很多地方也不太符合我们公司工程师口味,所以也没有继续执行下去。

    最近我们在开始整理各种技术规范和流程,会经历以下流程:

    • 内部主要人员(主要是是几个 TL )进行初稿审定,提出问题、分析问题、指定初稿。
    • 形成初稿后不仅在公司内部公示,不涉密部分还要在网上公示(重点)
    • 接收大家的意见和建议,综合各方意见并权衡利弊,确定一种方案开始在各团队中试行。
    • 后续再开发中遇到问题后,再升级规范。

    今天我们公示一下我们公司的初稿,并解释其中的原因,也广泛征求大家的意见和建议,大家的每一条建议我们都会深入思考和论证的。后续我们还会有很多类似的规范拿出来和大家一起交流,不吝赐教。

    后端 API 返回值规范初稿

    规范针对群体

    服务端、服务端对接端(网页端、APP 端、小程序端)

    规范细节

    • 正常的后端接口响应,HTTP Status Code 默认返回 200,只有在下列情况时接口才会出现非 200 的情况:
    1. 当服务端鉴权失败时返回 HTTP Status Code 401
    2. 中间环节三方软件返回的标准错误码,例如 Nginx 返回的 5** 错误码
    • 当接口 HTTP Status Code 为 200 时,返回的结构如下:
    {
      "resultCode": 200,
      "data": null,
      "errorMsg": ""
    }
    
    1. resultCode 表示服务端处理结果代码,用 3 位数字表示通用处理结果代码,用 4 位及以上数字表示业务处理结果代码,通用处理结果代码有:
      200 表示处理成功
      400 表示参数错误
      500 表示服务器内部错误
      503 表示当前服务不可用
      504 表示网关超时
    2. data 表示返回的数据,如果没有数据时返回 null
    3. errorMsg 表示错误时的返回信息,如果没有错误时返回空字符串,始终不能为 null
    • 当接口 HTTP Status Code 非 200 时,返回的结构如下:
    {
      "resultCode": {通用处理结果代码},
      "data": null,
      "errorMsg": "对应的错误消息"
    }
    
    • 三方软件 HTTP Status Code 非 200 时,我们对内容不可控,对接端要做好拿不到返回 body 的准备

    针对初稿里面的若干问题思考,也希望大家多提意见和建议

    • 为什么我们的返回都尽量采用 HTTP Status Code 为 200 ?
      答:经过公司内部对接端同事们的意见,如果返回的是非 200 状态码,目前很多框架会在框架层直接拦截,不会进一步解析消息体,不方便拿到服务端返回的错误消息。

    • [重点讨论] 那为什么鉴权失败时要返回 HTTP Status Code 401 呢?
      答:目前我们的 APP 中有通过 WebView 嵌套网页,网页里面又有 Ajax 的调用。

    之前我们的流程是,APP 内如果需要打开网页,则需要通过一个固定的链接进行转换,例如本身要访问 a 链接,则务必访问跳转链接并添加参数 b?targetUrl=a,同时携带 APP 的鉴权参数( APP 我们是把鉴权参数放到 header 里面的),服务端拿到 APP 的鉴权信息后会回写 cookie 到网页中,同时引导跳转到真正的目标地址 。

    上述流程里面就会出现一个问题,当 APP 里面的鉴权信息( Token )没有过期,但网页中的鉴权信息过期了(例如一些不可抗拒因素导致某台服务器丢失了网页端的 session ),用户再点击网页里面的内容,则始终会报告失败,给用户一种很不好的体验。

    之前我们是通过 bridge 的形式由网页端通知 APP 端发起重新跳转,如果本次跳转 APP 鉴权信息依然有效则按照前述流程正常打开网页,如果本次跳转发现 APP 的鉴权信息也失效了,则引导用户在 APP 中重新登录。 根据我们的经验,bridge 这种方式很容易出现兼容性问题,在某些机型上无法通知 APP,导致用户始终卡在网页上(用户并不知道要重启 APP,只知道找客服)

    为了让用户得到更好的体验,我们打算在 APP 层面捕捉 webview 的所有请求,一旦发现 HTTP Status Code 为 401 的情况,就引导 APP 重新登录,简单粗暴,不过会忽略 APP Token 有效,网页 Token 无效这种情况。

    如果把 401 放到返回 body 里面,不方便 APP 统一捕捉返回信息。

    • 针对 APP 打开网页,网页里面有网页跳转或者接口请求这种情况,大家还有更好的方案吗?
      答:我们的目的是能够更好地处理错误流程。也希望大家多多指导,不胜感激。

    如果按照上述我们的方案,我们的接口需要接收 cookie 这种鉴权方式( APP 内打开的网页通过 ajax 调用),也需要接收 header 这种鉴权方式( APP 直接请求);我们的网页需要接收 header 这种鉴权方式( APP 通过跳转链接打开目的网页时),也需要接收 cookie 这种鉴权方式(正常情况下都通过 cookie 认证)。

    第 1 条附言  ·  2019-10-30 16:55:30 +08:00
    我们会持续发布更多的讨论,把我们遇到的问题,以及怎么抉择的写出来,让更多人参与讨论,我们可以从大家的指导中收获到更多的知识。

    以下是资源放出来的讨论:
    /t/612902
    第 2 条附言  ·  2019-10-30 19:51:26 +08:00
    其实大家有讨论,才能发现各自的知识盲区,欢迎大家多多讨论。在这个帖子里面我就学习到了很多知识,不过最终指定出何种方案,都没有错,思考方向不同、业务不同,没有完整的答案,毕竟大家都是在造轮子而已。
    第 3 条附言  ·  2019-10-31 10:11:33 +08:00
    经过讨论和思考,我们决定先把网关做一个 demo 出来,然后各个对接端再进行验证,既要解决我已知的一些体验不好的流程隐患,又要能够让大家都同意,这样才能继续推动。

    其实返回值看似简答,但我们要考虑各个端如何配合,特别是 APP 内嵌网页进行 Ajax 调用的情况,非常令人头疼。已知的有微信重复刷新的问题、webview cookie 处理失效的问题,等等

    等我们把 demo 做完了,并确定了返回值和各端的交互方案,我们再公布最终的标准。

    感谢关注,届时再请大家来指导工作。
    27 条回复    2019-10-30 20:44:27 +08:00
    linxl
        1
    linxl  
       2019-10-30 15:17:22 +08:00
    "为什么我们的返回都尽量采用 HTTP Status Code 为 200 ? "
    这个怕是乱讲吧. 哪个框架?
    Vegetable
        2
    Vegetable  
       2019-10-30 15:18:22 +08:00
    我们采取的方案是 h5 和 app 共用 token,打开 webview 的时候使用 webview 的某个 hook,在 url 末尾拼接 token={token},对页面进行鉴权。
    页面内部的 ajax 从 url 上取当前 token,取不到自然走异常流程,能取到则正常发起 ajax 请求。
    odirus
        3
    odirus  
    OP
       2019-10-30 15:19:48 +08:00
    @linxl

    感谢反馈,这个问题,我会跟进的。
    odirus
        4
    odirus  
    OP
       2019-10-30 15:22:24 +08:00
    @Vegetable 这倒是一个不错的方案

    APP 跳转到网页,网页里面有 ajax 这个似乎都能够处理。那网页里面打开其他链接地址是否也可以呢?例如通过 js 拦截点击请求,把 Token 也添加到目标地址里面。

    非常感谢这个方案,非常有建设性的意见。
    baiyi
        5
    baiyi  
       2019-10-30 15:23:02 +08:00
    感觉接口规范的问题都要成周经贴了

    我先来:用 RESTful 规范!
    odirus
        6
    odirus  
    OP
       2019-10-30 15:23:42 +08:00
    另外,我刚刚和同事沟通了,网页通过 bridge 的方式通过 APP 或者小程序都比较稳定,没必要通过统一拦截返回状态码的方式来发现是否 Token 是否过期。
    odirus
        7
    odirus  
    OP
       2019-10-30 15:25:43 +08:00
    @baiyi

    非常感谢你的建议。

    其实很多时候都不是那么理想化,推进一个东西不仅要科学,更多的时候还要考虑人为配合的问题,也许你这个方案非常好,但别人就是故意不配合,那也很尴尬的,所以我们要征得内部大多数人的赞同才行。

    当然放到 V2EX 讨论是希望能够得到更多的意见和建议。
    ochatokori
        8
    ochatokori  
       2019-10-30 15:36:06 +08:00 via Android   ❤️ 1
    你确定鉴权失败要返回 401 吗…
    浏览器遇到 401 会弹出那个浏览器自带的输入帐号密码的东西
    star7th
        9
    star7th  
       2019-10-30 15:39:37 +08:00   ❤️ 1
    不用用 http 本身的返回状态。全部状态在业务层返回(也就是返回的 json 中)。这种兼容性是最好的,而且统一。
    odirus
        10
    odirus  
    OP
       2019-10-30 15:39:40 +08:00
    @ochatokori 嗯,非常感谢你这个反馈,我马上测试一下,我之前还真不知道。

    所以说把问题拿出来分享有很多人好处,避免闭门造车。

    最终我们会把所有规范和流程能开放的都开放出来。
    L1shen
        11
    L1shen  
       2019-10-30 15:43:54 +08:00   ❤️ 1
    make http code again
    zagfai
        12
    zagfai  
       2019-10-30 15:49:43 +08:00
    嗯 我们也是这样 实践上比 restful 好 至于好在哪里 别问我。
    luckyrayyy
        13
    luckyrayyy  
       2019-10-30 15:55:32 +08:00
    到底是用 http code 还是返回值,这个真的是每次拿出来都能吵三天三夜...
    SilencerL
        14
    SilencerL  
       2019-10-30 15:59:35 +08:00   ❤️ 1
    @ochatokori #8
    @odirus #10

    如果需要 Status Code 返回 401 并且不想让浏览器弹窗,在 Response 的头里面去掉 [WWW-Authenticate: Basic realm="Realm"] 即可。
    oott123
        15
    oott123  
       2019-10-30 16:41:57 +08:00
    与其说这是规范,不如说这是对你们现有架构的总结。
    通篇只看到说现有业务逻辑怎么怎么样,而不是设计成怎么怎么样会有什么好处或者坏处
    kkkkkrua
        16
    kkkkkrua  
       2019-10-30 16:47:13 +08:00
    怎么又是 http code 又是 body 中的 code ?
    当 http code 不是 200 的时候,应该都不用关注 body 了
    当 http code 为 200 的时候,body 中的 code 为业务 code
    restful 规范确实好,但是实现起来想达到理想状态,比较困难
    warcraft1236
        17
    warcraft1236  
       2019-10-30 16:50:22 +08:00
    这个东西看看各种大厂的 openapi 怎么做的不就行了?
    odirus
        18
    odirus  
    OP
       2019-10-30 16:52:22 +08:00
    @kkkkkrua

    经过商议和讨论,我们还是希望遵循一个大原则:就是除非 Token 失效返回 HTTP Status Code 外,其他情况都返回 200,但业务在处理参数校验、未处理异常这些公共错误时,还是要通过 resultCode 传递给调用端,让他们知道是怎么回事,方便给用户进行提示。
    odirus
        19
    odirus  
    OP
       2019-10-30 16:53:04 +08:00
    @oott123

    嗯,对的。我们会陆陆续续把我们要指定的各种规范和架构能开放出来的都开放出来,让更多人参与讨论和思考。
    x537196
        20
    x537196  
       2019-10-30 17:01:00 +08:00
    在 HTTP 状态码为 200 的情况下,我们是用业务码来标记业务处理结果,如果 HTTP 状态码非 200,一律提示服务不可用
    odirus
        21
    odirus  
    OP
       2019-10-30 18:02:05 +08:00
    @Vegetable

    我们也做了一些讨论,觉得你的方案很不错,当 APP 访问网页时就把 token 放到链接中,当网页里面打开其他链接时也把 Token 带上。

    但有一个问题,如果用户不小心把这个东西分享出去了,那就很危险了,相当于泄露了这些信息。

    你们是如何处理这个问题的呢?望解答,谢谢
    diferent
        22
    diferent  
       2019-10-30 19:32:00 +08:00   ❤️ 2
    很简单, 就是做的系统不够大, 不够复杂,所以才会不理解 Restful. 才会觉得所有东西都放到 Body 里最简单.
    Restful 的精髓就在于, 当你的系统足够复杂的时候,有 N 多层代理和路由服务时,不需要解析 Body(你也解析不起), 就可以追踪业务和进行业务分发或日志记录.
    Varobjs
        23
    Varobjs  
       2019-10-30 19:39:36 +08:00 via Android
    每月一次
    好用就是规则,大家遵守就好,没啥讨论了
    odirus
        24
    odirus  
    OP
       2019-10-30 19:49:56 +08:00
    @diferent 所言极是。
    mogutouer
        25
    mogutouer  
       2019-10-30 19:52:40 +08:00
    去看 twitter,facebook 的 API 格式不就行了
    Vegetable
        26
    Vegetable  
       2019-10-30 20:16:03 +08:00
    @odirus 我们的场景没有分享功能
    我认为只要能将客户端的身份信息传递给到 webview 就可以.url 不行还可以试试使用 http 的其他部分,比如 header 和 cookie.

    我了解了一下,安卓和 ios 好像都可以拦截 webview 内部的请求添加 header,cookie 应该也是可以的.涉及到客户端那边的知识我就不太了解了,你们可以按照这个思路讨论一下,如果内部页面比较复杂,尤其是涉及到第三方页面,可能会有一点麻烦.
    odirus
        27
    odirus  
    OP
       2019-10-30 20:44:27 +08:00
    @Vegetable

    好的,谢谢了。目前我们 APP 嵌入的网页也要求能够在浏览器里面正常打开,正常分享。所以有很多考虑。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1111 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 22:48 · PVG 06:48 · LAX 14:48 · JFK 17:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.