大家工作中的前后端是如何合作的?如何减少接口变更? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
crclz
V2EX    Java

大家工作中的前后端是如何合作的?如何减少接口变更?

  •  1
     
  •   crclz 2020-06-16 11:10:03 +08:00 7149 次点击
    这是一个创建于 1995 天前的主题,其中的信息可能已经有所发展或是发生改变。

    引言

    首先我大量查阅了 V2EX 、知乎的讨论,发现前后端主流做的合作方法都是:

    1. 前后端先商定一份接口文档(如果自动化一点,那么可以写一堆未实现的方法,然后用 swagger 生成文档)
    2. 接着,前后端并行地开发,后端实现接口,前端依据接口文档进行开发
    3. 前后端联调

    问题

    但是,在实践中,我发现了一些问题:

    起草文档阶段往往是非常暂的。在这个过程中,开发者无法了解系统全貌,自然无法确定完接口列表。随着前端和后端的开发工作的推进,这些忘记的接口、需要修改的接口才逐渐暴露,这会增加前后端合作、沟通的时间成本。

    那么,在起草文档阶段,是否有一个系统的方法,来确保最小化这种需要增加、修改的接口数量?

    51 条回复    2020-06-17 14:09:27 +08:00
    pmyile
        1
    pmyile  
       2020-06-16 11:16:06 +08:00
    交互评审的时候 走一遍 接口交互
    gz911122
        2
    gz911122  
       2020-06-16 11:17:00 +08:00
    在起草文档阶段,是否有一个系统的方法,来确保最小化这种需要增加、修改的接口数量 :

    后端先定好技术方案, 再商议接口文档,变更会小很多.
    pushback
        3
    pushback  
       2020-06-16 11:23:31 +08:00
    不改需求不就没修改了么。
    hantsy
        4
    hantsy  
       2020-06-16 11:24:30 +08:00   1
    对于开发阶段变化太快了,又臭又长的文档根本不可能用来做约束。

    CDC 模式( Consumer Driven Contract )是一种很好的用于 API 协议开发 Pattern,API Producer 和 Consumer 之间先定好协议 Contract,生成 Stub 之类的,再分开开发实现,最终两边都是要通过相关的 Contract 测试。

    自己查看 Pact 使用,支持各种语言。

    另外 Spring 下的 Spring Cloud Contract 也一样,对 Spring 生态支持一流,还可以定义 Message Broker 之间的协议,只是对前端语言框架支持不如 Pact 好。
    yidinghe
        5
    yidinghe  
       2020-06-16 11:27:14 +08:00 via Android
    前端只关注布局、风格、交互效果和响应性,内容通过后端发来的元数据渲染出实际的界面元素,比如表单有哪些字段等等。
    Kilerd
        6
    Kilerd  
       2020-06-16 11:38:00 +08:00
    果然会出现 spring cloud contract,那玩意写起来简直是灾难,然后团队无一例外的转向了 moco
    rapiz
        7
    rapiz  
       2020-06-16 11:52:34 +08:00
    突然想,有没有这样的工具,在开发初期可以用图形界面之类快速设定接口格式,可以生成 openapi 文档,然后向前端提供后端的 mock 代码,向后端提供对应框架自动生成的接口代码。
    carbrokers
        8
    carbrokers  
       2020-06-16 12:00:46 +08:00
    歪个楼,做过一个项目,后端把数据库所有能取到的字段全部给前端了,后期完全不需要接口变更,所以后续的开发任务后端完全没有参加过。。。
    skypyb
        9
    skypyb  
       2020-06-16 12:03:41 +08:00 via Android
    产品:新加一个小需求 /小改动
    xiangyuecn
        10
    xiangyuecn  
       2020-06-16 12:07:15 +08:00   2
    前后端靠背坐:
    前端要数据转身拍拍后面那家伙:兄 di 给数据
    后端改了接口转身揉揉后面那妹纸:接口字段又改了哈

    有争论?互殴解决
    faceRollingKB
        11
    faceRollingKB  
       2020-06-16 12:12:38 +08:00
    个人经验:这种问题几乎无法避免,所以技术上我选择用 typescript 开发,方便日后改接口定义,另外如果接口定义问题比较大(概率小),通常涉及到组件、模块的重构,所以我选择用 ng 框架,重构代价小
    Jackeriss
        12
    Jackeriss  
       2020-06-16 12:14:10 +08:00 via iPhone
    graphql
    chendy
        13
    chendy  
       2020-06-16 12:17:08 +08:00
    @Kilerd #6 +1 spring 家两个东西 restdoc 和 contract 都是理想很丰满,用着贼难受的东西
    lenqu
        14
    lenqu  
       2020-06-16 12:26:00 +08:00
    可以参考我之前未做完的一个的东西,API 接口的一个中间件,在后端接口之上进行统一规范化,后端只完成最小单元接口实现,中间件进行整合提供服务

    有需要的联系我哦!!!一起做
    Kilerd
        15
    Kilerd  
       2020-06-16 12:31:27 +08:00
    @lenqu #14 graphql ? 先解决 N + 1 的问题再说吧。
    Foxkeh
        16
    Foxkeh  
       2020-06-16 12:32:17 +08:00
    @carbrokers 你说的"所有", 包括用户的密码字段吗
    malusama
        17
    malusama  
       2020-06-16 12:32:55 +08:00
    @Kilerd DataLoader
    lenqu
        18
    lenqu  
       2020-06-16 12:39:20 +08:00
    @Kilerd 如果已有的后端接口可以实现,那么直接扩展即可,如果没有,在已有的单元接口之上再进行扩展,保证后端开发代码量最小
    Justin13
        19
    Justin13  
       2020-06-16 12:41:26 +08:00 via Android
    上 GpaphQL
    hantsy
        20
    hantsy  
       2020-06-16 12:51:39 +08:00
    @chendy 这两个东西只要你的团队一直坚持写测试,一般不会有什么太大的问题。restdocs 我之前一个项目下来一直在用,用于 API 开发文档生成。

    Spring Cloud Contract 有一定局限性,对于全部后端 Microservice 中 service 2 service 互操作的测试,优势很明显。但前后端调用,虽然官方也一直在加强其它实语言支持,也提供了 Nodejs 与 Java 项目结合的例子,但是与前端结合的时候远不如 Pact 舒服。
    slyang5
        21
    slyang5  
       2020-06-16 12:52:58 +08:00
    用 protobuf ?
    hantsy
        22
    hantsy      2020-06-16 12:54:00 +08:00
    @Kilerd Moco 是什么鬼???
    ChanKc
        23
    ChanKc  
       2020-06-16 13:05:01 +08:00 via Android
    理论上,restful 和 hateoas 就是为了解决这个问题的。
    当然实际上,restful 已经变成了“有比较好看的 URL 的 API”的意思了
    ty89
        24
    ty89  
       2020-06-16 13:17:09 +08:00
    @carbrokers 这样别人爬你数据就太方便了,简直就是直接复制你的数据库
    hantsy
        25
    hantsy  
       2020-06-16 13:18:07 +08:00
    @chendy 以前我觉得比较难受是用 Json Schema 定义( jsonschema.org ,这个也是标准 draft)把交互格式都是定好。

    有在线工具 https://jsonschema.net/home

    然后前后全部写测试验证符合就行了,这个比较直接。

    后端 Java 可以用 RestAssured (包括 Jsonschema vilidator)验证.

    前端按 JsonSchema 定义生成 json data 文件测试中 mock 就可以了。
    ty89
        26
    ty89  
       2020-06-16 13:19:24 +08:00
    从根本上的解决方案应该是 TDD, 测试驱动开发。 尤其是后端。
    hantsy
        27
    hantsy  
       2020-06-16 13:33:57 +08:00
    @ty89 这是必须的,对 API 开发很容易的事情。

    如果以前传统 Web 页面测试难写,说得过去,国内的项目页面变化太化,页面设计 Elment 不标准。现在纯 API 测试真的很简单。
    zzzmh
        28
    zzzmh  
       2020-06-16 13:45:08 +08:00
    1. 生成文档,我们用 swagger,另外还有一个 apidoc 生成放到静态服
    2. 我要是能帮前端写掉一部分的代码尤其是逻辑类的,我会实现一版 demo
    3. 再有分歧就互掐
    houfeibin
        29
    houfeibin  
       2020-06-16 13:46:08 +08:00
    @carbrokers 感觉你这样数据会很不安全
    hantsy
        30
    hantsy  
       2020-06-16 14:04:28 +08:00
    @zzzmh Swagger 结合其它的一些工具,也可以生成不错的静态文档,https://github.com/hantsy/building-restful-apis-with-springmvc-gitbook/blob/master/swagger.md#documents-rest-apis
    noobsheldon
        31
    noobsheldon  
       2020-06-16 14:07:01 +08:00
    一个接口,通过接口参数区分逻辑,完美。
    noobsheldon
        32
    noobsheldon  
       2020-06-16 14:07:45 +08:00
    ISSSSSSS
        33
    ISSSSSSS  
       2020-06-16 15:03:20 +08:00
    所以必然要有联调试过程。
    index90
        34
    index90  
       2020-06-16 15:15:08 +08:00
    如果一个软甲只加代码不修改,是几乎不会出错的。接口也一样,不存在修改接口,只有加接口,用 /v1/xxx /v2/xxx 去区分。
    la2la
        35
    la2la  
       2020-06-16 15:33:27 +08:00
    后端开发搬着笔记本做到前端小姐姐旁边调
    Seneca
        36
    Seneca  
       2020-06-16 15:35:23 +08:00
    我们是后端写接口,前端等着接口来了,直接调用就行。给你啥你用啥
    xff1874
        37
    xff1874  
       2020-06-16 15:45:40 +08:00
    我们目前正在做这个事情,原理就是读取定义的接口和真实的请求,然后做比对
    stevenkang
        38
    stevenkang  
       2020-06-16 16:38:14 +08:00
    需求来了,基本上过一遍就能把接口定下来。

    要改接口一般都是由于需求不明确(或自己没理解到位)、需求变更等原因。

    这种情况下改一下接口就行了,毕竟改需求前端后端不都得过一遍嘛。
    lplk
        39
    lplk  
       2020-06-16 16:59:43 +08:00
    @carbrokers #8 我就这样搞过,虽然很省事,但是如果后期表格字段不断增加,这样会增加网络传输数据的时间,甚至有的字段会很大,最好还是严格规范
    msg7086
        40
    msg7086  
       2020-06-16 17:02:41 +08:00
    改接口 = 改文档,然后前段照着文档来不就行了。
    BDD/TDD 很多框架都可以根据测试数据来生成文档的。
    jinwyp
        41
    jinwyp  
       2020-06-16 17:04:38 +08:00
    很简单啊 找个资深前端定接口, 前端把界面开发完成 50%后 定的接口基本差不多了。
    Aprilming
        42
    Aprilming  
       2020-06-16 17:05:22 +08:00
    前端就给我画样式,接口啥的都是自己来写,自己对接,前端逻辑也是自己写。我的前端伙伴就是一个无情的 UI 机器人
    Airon
        43
    Airon  
       2020-06-16 17:08:35 +08:00
    交互能明确确认,确认完交互后端就能先出 mock,这样前后端开发就没啥影响。但是问题在于,需求是否真的明确这么细,产品是否完全不变动交互流程 (ps:很多傻逼产品明明自己确认的交互,开发出来还逼逼赖赖 技术们开发不考虑用户体验。。。然后又是改流程改接口)
    xuanbg
        44
    xuanbg  
       2020-06-16 17:09:49 +08:00
    起草文档阶段往往是非常短暂的,这个是有问题的。设计的必要时间还是要留的,好的设计能够有效减少写代码和测试的时间。所谓磨刀不误砍柴工。
    lenqu
        45
    lenqu  
       2020-06-16 18:23:56 +08:00
    @ty89 干过 DBA 的都知道,数据库的一个普通权限读权限用户想拿到写权限需要找漏洞,反爬从来不在后端逻辑里写入不是么?
    Sapp
        46
    Sapp  
       2020-06-16 20:02:51 +08:00
    你想要一次性拿全接口,如果是小项目开发有可能,只需要找个经验丰富的后端,他基本能 cover 你所有的需求,提供的接口一点不会少,妥妥的够你用,你想到的问题他都提前想到了,但是大项目基本不可能,一是你没办法招到那么多好后端,二是大项目本身就面临着需求变更,如果一开始就商量好所有的变更,我不说现实不现实,你们坐着开会都要开多久?开会的时间都可能比开发的时间长, 更何况真的能坐在会议桌就考虑到所有的问题? 所以这方面就不要指望能完全搞定了。但是有另一个方法,可以减少接口变更带来的前后端成本,就是 typescript + 动态生成接口,前端不需要手写 ajax 请求,直接调用 node 根据后端文档生成好的请求方法,
    例如:

    const [getUsername, loading] = useRequest(getUsernameRequest)

    useeffect(() => {
    getUsername({userID: xxx})
    }, [])
    getUsernameRequest 直接生成的,同时生成了 interface 文件。
    同时因为 typescript 的类型要求,前端也根本不需要看文档就能知道需要输入那些参数,并且你提到的后端改了接口,把 userID 改为了 userId,那么前端会直接收到 typescript 的报错提示,然后顺手改一下就行了。如果是改了接口名字,那么前端看一下 git 记录就知道你改了哪些名字,也不需要在口头沟通,或者等测试报错才知道你改了接口。
    这样对于后端而言省了每天提醒前端我又改了什么什么的时间,对于前端省了每天要找后端对你又改了什么什么,怎么我测试的时候还没问题,测试一测就又出问题的时间,还少写了 interface 文件。
    6IbA2bj5ip3tK49j
        47
    6IbA2bj5ip3tK49j  
       2020-06-16 20:14:58 +08:00
    spring cloud contract 真的是垃圾,道理我都懂,用起来就是难用。
    一个简单的
    json array 为空列表 -> 合法
    json array 不为空 -> 判断里面 item 字段
    都做不到(只能通过很恶心的办法实现
    zqx
        48
    zqx  
       2020-06-17 06:52:33 +08:00 via Android
    需求评审-设计交互评审-前端和后端分别技术评审,然后再出接口文档,再开发,基本不会改了,除非产品需求变更了
    ty89
        49
    ty89  
       2020-06-17 09:59:11 +08:00
    @lenqu 你把数据库里的全部字段直接通过接口返回,那不就相当于直接复制你数据库表了吗,跟读写权限有啥关系。
    hejingyuan199
        50
    hejingyuan199  
       2020-06-17 11:20:54 +08:00
    我没有认真读前面的留言。不好意思。

    我们的做法是,后端先把基础弄起来后。
    前端给我们一个接口清单,我们去做。
    毕竟前端跟客户最贴近。
    不过如果遇到前端太胡闹的时候
    就得吵吵架。

    让前端提要求以后,
    他们就不太 BB 地改需求了,
    毕竟理亏在他们。
    sunxiansong
        51
    sunxiansong  
       2020-06-17 14:09:27 +08:00
    与其避免接口变化不如拥抱变化,让变化可控、可测试、可追踪

    - 首先会有一份 api 风格说明,说明一般的通用的数据结构风格、错误处理、token 机制
    - 后端通过 cli 工具导出后端模型到前端的 ts 结构,前端可以拷贝或直接使用数据模型。导出的代码也包含了可能的错误码、常量枚举
    - 后端直接写 ts http 调用的代码,并附上最小可测试代码( jest 测试代码),在代码中标注文档(状态码、错误码等),这步其实主要是 route 标注,前端甚至可以直接复用 http 调用的代码
    - 生成的 ts 数据结构和 http 调用代码放在 git 上,提交时填写恰当的注释标注 api 变更,这步主要是确保前端可以详细的跟踪 api 变动,前端可以 watch 文档工程追踪 api

    - 时间充足的话,最好还是写足够的 api 集成测试


    我以前还做过其他的尝试,在测试环境用 AOP 拦截请求,用 json schema 记录请求的 route/request body/response body,然后写到数据库里,再人工标注 api 注释,缺点就是首次请求之前不会有记录。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2970 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 14:00 PVG 22:00 LAX 06:00 JFK 09:00
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86