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

请教一个涉及前向兼容的 API 设计问题

  •  
  •   xFrank · 58 天前 · 1892 次点击
    这是一个创建于 58 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景:
    我们提供一个 SDK ,有很多 API 供上层应用使用。每次 API 变更都要考虑前向兼容,不能影响已有应用(否则代价巨大)

    问题: 假设我有一个 API ,设为 DoA ,当 DoA 出现异常的时候,会通过另一个回调函数 OnError(err)返回错误信息给上层应用。 假设前期的错误码回调设计是这样的( err 是个结构体,包含 code 和字符串):

    {1001, "原因 1"},
    {1002, "原因 2"},
    {1003, "原因 3"}
    

    最近有部分用户提出来,部分错误码设计不合理:原因 2 太粗,需要细化为原因 2a ,2b ,2c 。需求是合理的。

    那这个时候整个 OnError 的错误码回调应该如何设计呢?(即能精细化错误码,又要保证前向兼容) 当前想到的几个方案:

    方案 1: 直接新增 code 和错误码字符串,

    {1001, "原因 1"},
    {1002, "原因 2"},
    {1003, "原因 3"},
    {10021, "原因 2a"},
    {10022, "原因 2b"},
    {10023, "原因 2c"},
    

    问题:会导致原来只判断 1002 的应用不兼容

    方案 2: 新增 code 和错误码字符串,当出现 2a 导致的错误时,回调两次,把 1002 和 10021 都回调一下 问题:上层应用不一定能接收两次回调,或者可能导致上层应用出现一些时序相关的奇怪问题

    方案 3: 不新增 code ,通过不同的字符串来标识精细化的错误信息。类似这样:

    {1001, "原因 1"},
    {1002, "原因 2a"},
    {1002, "原因 2b"},
    {1002, "原因 2c"},
    {1003, "原因 3"},
    

    问题:对开发者不太友好,因为他还需要去解析字符串。。。

    大家看下是否有更好的设计?

    第 1 条附言  ·  58 天前
    澄清下,这个场景比较特殊,只要有升级新版本,所有应用都只能跑最新版本。。。
    向前向后概念在中文环境里面是有点乱,大家知道意思就行
    27 条回复    2024-07-12 20:20:27 +08:00
    sujin190
        1
    sujin190  
       58 天前
    感觉比较好的方式是添加一个 error_details 数组结构化字段,里边详细包含各种细节错误信息,一级的 error_code 只用来表示错误大类别,一般来说 error_code 决定大业务流程方向,在保证 error_code 能完整完成业务流程情况下,error_details 提供细节优化,实际情况下就算后续 error_details 进行调整了不兼容也能保证业务流程能正常完成,只不过纠错、提示或者交互不那么优化而已,毕竟大的业务流程总不能说改就改吧,否则你这兼容性感觉没法弄
    iOCZS
        2
    iOCZS  
       58 天前
    一般错误有个域的概念,域下面才细分具体的情况,域可以用一个数据范围代替,譬如说 11000-11999 都是某个类型的错误
    BiChengfei
        3
    BiChengfei  
       58 天前
    方案二吧,新增加一个字段,例如原字段:code ,新字段 codeDetail 。只是不明白为啥方案二要分两次回调
    13240284671
        4
    13240284671  
       58 天前
    v1,v2 版本
    Curtion
        5
    Curtion  
       58 天前
    你的三个方案都要求上层应用做修改才行,这也没有兼容上啊。看起来发布新 SDK 就行, 需要的人更新不需要的人继续用旧版,想升级的人也改(反正他们都得改)
    ntower
        6
    ntower  
       58 天前
    话说你这是向后兼容吧
    javalaw2010
        7
    javalaw2010  
       58 天前
    新增一个 subcode
    gaobh
        8
    gaobh  
       58 天前 via iPhone   ❤️ 1
    大部分都是提供 v1 v2 接口,老接口不断,直到大部分人都迁徙
    zhuisui
        9
    zhuisui  
       58 天前
    首先:你这是向后兼容,不是向前兼容。。。
    xFrank
        10
    xFrank  
    OP
       58 天前
    @sujin190 error_details 添加在哪里啊?
    @BiChengfei 因为要给老应用回调老 code ,新应用回调新 code
    @Curtion 方案 3 可以兼容老的,因为从开发角度看,一般人 if else 里面写的都是 code ,那个字符串都是用来打印提示信息的。方案 3 就要求新的应用纪要判断 code ,也要提取字符串里面的信息来做更多的判断

    另外,澄清下,这个场景比较特殊,只要有升级,所有应用都只能跑最新版本。。。
    xFrank
        11
    xFrank  
    OP
       58 天前
    @iOCZS 你说的错误码分级的情况,当前接口已经已这样的形式开出去了,不能在改了。。。
    ilvsxk
        12
    ilvsxk  
       58 天前
    抛弃用 int 来定义 error code ,用字符串,用 . 来表示业务层级关系,比如:{ "error": "order.edit.network_error"} {"error": "order.edit.no_auth"} ,既表意,又能扩展,也有层级关系。
    whoosy
        13
    whoosy  
       58 天前
    @ilvsxk 你这... op 的重点是“兼容”两字
    whoosy
        14
    whoosy  
       58 天前
    影响面最小的修改就是方案三
    vacuitym
        15
    vacuitym  
       58 天前
    不如增加一个二级的 code 和 msg
    linauror
        16
    linauror  
       58 天前
    推荐 sub_code 的形式
    magicls
        17
    magicls  
       58 天前
    我多问一句,你写的
    ``` json
    {1001, "原因 1"}
    ```

    这是你简写了,还是说就是这样的?因为我理解接口再怎么烂,它不应该都是

    ``` json
    {"code":1001, "message":"原因 1"}
    ```

    这样吗?
    ZZ74
        18
    ZZ74  
       58 天前
    建议用 3 ,问题本质是错误原因定义太宽,2a 2b 其实就是不同错误
    ilvsxk
        19
    ilvsxk  
       58 天前
    @whoosy #13
    兼容的话,不管是新增错误码,还是用子错误码还是别的方案,要做兼容都是搞一套新的或者加新的,旧的不动,继续留着。
    renmu
        20
    renmu  
       58 天前 via Android
    以前没考虑到,那现在不管你怎么改都会改变原来的语义,那就不要改了,留着以后 v2 大版本不兼容再动
    esee
        21
    esee  
       58 天前
    好麻烦,就算我提供了兼容的 api ,也不知道使用者对返回的结果是怎么使用和判断的,可能又会引发问题。
    所以涉及到返回结果数据结构需要变动的话,我都是直接接口 V1 ,接口 V2.。。这样写新的。。
    BiChengfei
        22
    BiChengfei  
       58 天前
    就 subcode 方案,我也是这个意思
    ryanking8215
        23
    ryanking8215  
       58 天前
    为啥不能 {1004: "2b"}
    dddd1919
        24
    dddd1919  
       58 天前   ❤️ 1
    设计 APIv2 ,用新设计的 code ,然后给 APIv1 打上 deprecated ,备注迁移说明
    cowcomic
        25
    cowcomic  
       57 天前
    如果结构体就是发出来这个样子,可以改成下面的形式
    ```
    {1002, "原因 2b", "errorCodes": [1002, 100202], "msg": "原因 2b"}
    ```
    1002, "原因 2b" // 这部分就给原来的系统用

    "errorCodes": [1002, 100202], "msg": "原因 2b" // 新系统用这部分,原来系统如果能迭代的话,逐渐改为这种
    "errorCodes": [1002, 100202] // 这个结构留着以后细化 code 用

    如果新结构想再增加扩展性,不同 codes 不同 msg ,外面再封一层数组

    想再再再增加扩展性,参考 Java 的 Exception 对象
    dyllen
        26
    dyllen  
       57 天前
    像楼上说的在原来的基础上增加子级错误码和消息,例子有支付宝也是这样干的,有两级,我接过好几个支付相关的接口都是如此。
    xFrank
        27
    xFrank  
    OP
       57 天前
    @cowcomic 尝试下这个,感谢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1062 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 43ms · UTC 23:24 · PVG 07:24 · LAX 16:24 · JFK 19:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.