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

后端接口如何做版本控制?

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

    假设有下面一个场景: 1.0 版本客户端的用户注册功能,手机号是可选填的,即使用户不输入手机号也能注册,对应的后端接口 URL 是 v1/reg

    但到了 1.1 版本,手机号改为了必填,这时候有两种做法:

    1 )做一个新的 v2/reg 后端接口给客户端使用,缺点是,需要同时维护 v1 、v2 两个版本的接口,而且会造成两个接口的代码大量冗余,除了“手机号是否必填”不一样以外,其它功能的代码都一样

    2 )让客户端把版本号传过来,后端接口根据客户端版本号来判断手机号是否必填,缺点是,接口里会充斥着很多对客户端版本号进行判断的代码,例如:

    if (appVersion == "1.0") {
        // do something
    } else if (appVersion == "1.1") {
        // do something
    } else {
        // do something
    }
    

    上面哪种做法更好呢?或者各位大神有没有更好的版本控制方案?

    49 条回复    2021-06-16 18:35:17 +08:00
    deplives
        1
    deplives   166 天前   ❤️ 1
    用路由去控制,domain/v1/xxxx domain/v2/xxxx 不要在业务代码里判断版本,这样后期维护成本太高了
    Huiao
        2
    Huiao   166 天前
    1.0 版本的代码和 1.1 版本的代码并存。网关通过 v1/v2 去转发
    wellsc
        3
    wellsc   166 天前
    header 加版本号
    Huiao
        4
    Huiao   166 天前
    1.0 和 1.1 的服务并存
    Leigg
        5
    Leigg   166 天前
    同 1 楼,不要在代码里面写 if elseif,会成为噩梦的开始,
    kaneg
        6
    kaneg   166 天前   ❤️ 3
    URL 加版本 /v1/abc, /v2/abc 。既要支持多版本,又要代码完全不重复是不可能同时满足的,鱼与熊掌不可兼得。只能尽量通过抽取公共代码的方式来消除重复代码。毕竟对外的 API 就是这种特性,一旦暴露即等同于固化。除非根本就不管兼容性的问题。
    ch2
        7
    ch2   166 天前
    通常的做法是 1
    initd
        8
    initd   166 天前
    GraphQL
    raaaaaar
        9
    raaaaaar   166 天前 via Android
    我看见比较多的是 1,代码抽象出来更好,加分支的话。。
    encro
        10
    encro   166 天前
    首先排除一个最简单做法:新增一个 reg2 方法。
    janus77
        11
    janus77   166 天前   ❤️ 1
    你这样无论如何都要有 2 套逻辑共存。注意我说的是逻辑,无论是客户端还是后端,所以你维护两块代码是不可避免的事
    fewok
        12
    fewok   166 天前
    有没有想过,这种不好做监控
    9yu
        13
    9yu   166 天前 via Android
    K8s 生态里面都是 /v1alpha/, /v1beta/ 这样的,短期可以同时保留
    chinvo
        14
    chinvo   166 天前 via iPhone
    正确做法是分开做 controller, 版本放到 url 或者 header 里
    Maboroshii
        15
    Maboroshii   166 天前
    新增一个 v2,然后 v1 除了修 bug 不维护了啊,后面的客户端更新就只能用 v2
    rockyliang
        16
    rockyliang   166 天前
    @kaneg 比较赞同你的看法,我目前能想到的也是只能通过抽取公共部分来消除不同版本接口之间的重复代码,感谢你的回答~
    rockyliang
        17
    rockyliang   166 天前
    @Maboroshii 新增一个 v2 的话,v2 和 v1 两个接口的代码就会大量重复,不过这种倒是可以通过抽取相同部分的代码来消除。v1 和 v2 两个接口只写差异部分的代码,相同部分的则通过调用共用函数 /方法来执行。感谢你的回答:)
    wd
        18
    wd   166 天前 via iPhone
    和客户端对接的时候一定要注意客户端那边肯定是新旧版本同时共存的,他们一般没有办法控制用户那边的更新情况。所以你这个答案很明显,必须是共存。除非说不支持旧版了。
    polyang
        19
    polyang   166 天前
    这种明显是用第一种方案。
    在代码里判断版本号会成为你噩梦的开始
    rockyliang
        20
    rockyliang   166 天前
    @wd 其实问题里我提到的两个方法都是可以做到共存的,差别就在于:第一种需要新增一个接口;第二种不需要新增,但需要在同一个接口里判断客户端版本,然后执行不同的逻辑。
    xckai123
        21
    xckai123   166 天前
    新增一个 v2,等到 v1 确定没有流量再进来后,下一次 release 再把 v1 删掉,v2 改为 v1
    rockyliang
        22
    rockyliang   166 天前
    @polyang 是的,前面回答的同学也是这么说,所以第二种我不会考虑了,哈哈。感谢你的回答
    axbx
        23
    axbx   166 天前
    网关做控制 根据账号来判断改走哪个接口
    yeqizhang
        25
    yeqizhang   166 天前 via Android   ❤️ 1
    我们之前用的 if 判断的,传的版本号应该用大于来判断而不是==,我寻思前面说用网关也是要写判断逻辑吧,只是换个地方写了
    Luckydan
        26
    Luckydan   165 天前
    前端完全可以在发送请求的时候
    Luckydan
        27
    Luckydan   165 天前
    前端在发送请求的时候无论是否手机号是必填项,都把手机号作为请求参数发到后端,后端根据手机号是否为空,再做对应的业务逻辑调用是不是稍微好点呢?
    eudore
        28
    eudore   165 天前
    新增一个接口很难??
    95276
        30
    95276   165 天前
    趁这个机会提醒用户升级
    WollensZhang
        31
    WollensZhang   165 天前
    header 中添加 api release 信息,同时在 controller 层做版本隔离
    ak47007
        32
    ak47007   165 天前
    @encro v3,v4,v5 是不是要新增 reg3 reg4 reg5 ?
    yufeng0681
        33
    yufeng0681   165 天前
    就你这个需求,手机号码在业务上已经要求是必填了。V1.0 版本的报错机制缺省就有, 后台判断不带手机号码,就返回报错,提示语: 手机号码必须填写。 不需要开发第二个接口 [针对你这个独特场景]
    Drc
        34
    Drc   165 天前
    👆🏻正解
    encro
        35
    encro   165 天前
    @ak47007

    本质上,其他办法只是换了个姿势加 reg3,reg4,reg5 。

    对于注册这种,如果是内部不对外,影响人群不大,变更较快,没有必要采用 version,直接加一个 regWithMobile 方法可能是最简单的。不要将几分钟的活,变成几天的。将这个 version 加到变更日志和接口文档即可。
    encro
        36
    encro   165 天前
    以微信公证号接口为例:1.0-1.4 接口 url 其实是不变的,只是新增了其他方法,比如新增了新的分享到朋友圈方法,原来的分享朋友圈方法规定到某个版本废弃。
    huifer
        37
    huifer   165 天前
    请求头
    Rocketer
        38
    Rocketer   165 天前 via iPhone
    用 api gateway,默认各版本路由到相同的 handler,只把不同的部分路由到不同的 handler,这样就没有冗余了
    rockyliang
        39
    rockyliang   165 天前
    @yufeng0681 这样会导致使用旧版本客户端的用户不能注册,相当于强迫用户要升级到新版本,用户体验很差。当然问题中的手机号也只是我临时想到的一个例子,而实际中肯定也还有很多场景是有着 [ 新版本功能规则做了变更,但不能影响到旧版本的用户 ] 的要求
    ltruntu
        40
    ltruntu   165 天前
    传入参数 加一个 判断参数 nginx 直接判断 1 就转发 1 的机器 2 就转发 2 的机器 类似灰度
    walker2laok
        41
    walker2laok   165 天前
    沟通好只维护 3 个或其它个版本 [v2 、v3 、v3.1] ,如果 v1 来了,就提示强制升级
    youngyezi
        42
    youngyezi   165 天前
    你这种情况,可以通过客户端上传版本号(版本号放 url,header 都可以)。项目代码里构造一个版本控制的中间件,通过中间件来转发:"把不同的版本转发到对应的版本下的 Action"。
    1.这样不需要增加网关等其他额外服务
    2.不需在每个方法体里做版本判断
    3.多版本共存,哪个版本有变动只需要修改对应版本下的方法
    beichenhpy
        43
    beichenhpy   165 天前
    网关根据版本来控制好一些
    beichenhpy
        44
    beichenhpy   165 天前
    或者 header + filter 处理一下就行
    samin
        45
    samin   164 天前
    业内做法:
    1. 两个项目,历史项目的接口用 /v1 标识区分,新项目用 /v2 标识区分,流量进入不同的项目互不影响
    2. 同一个项目中,和 1 是一样的,只不过用不同的包来存放接口,一样用类似的方法来区分

    不管哪种方式,谨记代码方法编写的`单一性`原则
    dcoder
        46
    dcoder   164 天前   ❤️ 1
    @rockyliang
    其实方法(1),(2)最后都需要保存 2 套逻辑, 所以一定要做的事情是: factor out 共用逻辑,减少重复代码.
    至于很多人说不能用(2), 其实也不一定, 因为很多小更新(v1.0.0 --> v1.0.1), 也不一定要新开个大版本号 (v1.0 -->v2.0)
    dcoder
        47
    dcoder   164 天前
    像这种小更新(v1.0.0 --> v1.0.1), 也不一定要重新定义一个 api/path/v1.0.1
    edk24
        48
    edk24   164 天前
    后台接口大多是要配置路由的,类似这段伪代码。多写一个接口就可以了

    ```
    web.get("/v2/user/register", function(req, resp){

    });

    web.get("v1/user/register", function(req, resp){

    });
    ```
    hewiefsociety
        49
    hewiefsociety   164 天前
    就是一楼那么做
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1193 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 18:43 · PVG 02:43 · LAX 10:43 · JFK 13:43
    ♥ Do have faith in what you're doing.