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

彦祖们,这种 API 接口设计有哪些利弊?

  •  
  •   ibegyourpardon · 2021-07-27 10:01:34 +08:00 · 5101 次点击
    这是一个创建于 1218 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在复刻一些产品做练手,发现了不少新产品的 web api 接口很有意思。两个典型参考对象是 wolai.comtodoist.com

    传统上,我们会根据业务需求给不同的业务设计开发出不同的 api 接口出来给前端调用。

    比如某个系统叫 V2EX,包含了对帖子的增删改查,我们往往会创建

    /article/create
    /article/update
    /article/delete
    /article/get/:id
    /article/list/get

    等接口。

    然后处理分类还有
    /category/create
    /category/update
    等等。

    当然其中还有一些可以合并的,具体不一而足,因人而异。

    但我最近看到了这样一些接口。使用方法类似于

    /article

    然后 payload 为

    {
    	"resource_type": "article",
            "transaction": [{
        	"requestId": "some kind of uuid"
        	"command":"updateArticle",
            "args": {
            	"articleId": some int or uuid,
                "content": "some string",
                "blahblah": "blahblah"
            }
        }]
    }
    

    这个 payload 本身不难看懂,其中 transaction 是个数组,可以一次包含多个指令。

    这个让我想起来以前我们有的时候开的玩笑,说根本不需要写很多接口,只要一个接口,传不同的参就可以干所有的事。现在看来这个玩笑不仅被人真的这么做了,还给了很完善的事务概念。

    但我仍然有些困惑,这样的模式真的很好用吗?

    我能想到一些优势,比如前面可以是一个很薄的 controller, 后面随着 args 的不同调用不同的 service 处理。service 可以很轻松的切换版本,不需要担心和前端的对接。队列在后端的应用也是显而易见的。

    对前端来说,也不再需要看 N 个接口的文档,大多数接口会统一起来有一套标准,只要定义约定好相关的行为和参数就可以。

    但这样的接口调用,是不是本身就太复杂了呢? 我大概想了下,似乎没有看到很明显的收益。我大概看了下相关的前端,有这样一个特性。

    比如说列表页,我新增一个内容,传统模式是先 create,然后再获取 list,进行 data 更新。 现在会改成先操作本地 data,直接在 list 里 append,然后再发一个事务描述,告诉后端去做对应的相关操作。

    我甚至在某个 app 中遇到过事务失败的问题,然后造成了多端登录的情况下有一个端死活同步不了( Todoist,事务 ID 对不上,死活不能同步数据。)

    就想问问各位大佬,这种接口设计除了我自己臆想分析的特征,还有什么别的是我没考虑到的吗?

    littlemcdull
        1
    littlemcdull  
       2021-07-27 10:12:13 +08:00
    我能想到的是这种接口挺适用增量更新的,比如,一段时间后服务端新增了若干条数据记录,有删除,有修改,有新增(比如在另外一个终端 iPad 上登录了同个帐号,进行了一些操作,然后又换了个 iPhone 设备同一个帐号,需要从服务端同步数据)
    Morriaty
        2
    Morriaty  
       2021-07-27 10:12:16 +08:00
    es 的增删改接口 bulk 也是这个设计方式,和传统的 restful api 有啥优劣,我还真感受不出来,都一样🤣
    javalaw2010
        3
    javalaw2010  
       2021-07-27 10:15:01 +08:00
    我个人感觉第二种接口会在应用有离线需求、多端同步的时候使用,比如笔记应用新增一篇文章,你肯定不能因为没有网络而不让用户新增文章,多端同步的时候,A 设备上的操作可能只有部分操作同步到了 B 设备,等到有条件的时候再把剩余的操作同步到 B 设备上,这种情况下第二种的方案会比较方便些。
    sudoy
        4
    sudoy  
       2021-07-27 10:15:25 +08:00
    只能说这操作太骚了,随着项目越来越庞大,这个看起来就很费劲了,不管前后端,维护性和可拓展性就慢慢变差了
    zjsxwc
        5
    zjsxwc  
       2021-07-27 10:15:49 +08:00
    方便一次接口请求,就能完成原先 restful 方式需要 N 次请求的场景。
    JerryCha
        6
    JerryCha  
       2021-07-27 10:24:25 +08:00
    可能它只是个 gayway,真接口隐藏在后面
    ibegyourpardon
        7
    ibegyourpardon  
    OP
       2021-07-27 10:30:00 +08:00
    @littlemcdull 其实这个模式我想过,颇有点类似 MySQL 了。
    要实现同步数据,一个是传统的模式,拿最终数据。
    二个是像这种模式一样,拿所有的操作行为日志,再完整走一次,就可以追回最终数据了。

    但我想了下,这种前后端的场景中,走日志的模式代价远比直接获取最终数据高。
    ibegyourpardon
        8
    ibegyourpardon  
    OP
       2021-07-27 10:31:39 +08:00
    @javalaw2010 对对对,这个问题我也想过。
    我甚至联想到了游戏服务器上,多玩家联网操作的逻辑同步。

    但引申来了一个新问题。
    我不同端操作先后有别,那我为了保证某种程度的最终一致性,后端除了记录数据,是不是还得记录下来自多个客户端的所有操作,类似 binlog 日志那样。
    感觉又增加了存储队列上的额外操作。
    dream4ever
        9
    dream4ever  
       2021-07-27 10:34:13 +08:00   ❤️ 2
    @JerryCha gayway ? gateway ?
    intmax2147483647
        10
    intmax2147483647  
       2021-07-27 10:38:08 +08:00   ❤️ 1
    用后者不如用 GraphQL (也有些区别),用前者不如用 http method 区分不同的接口,写个 /create /get 在 URL 里面显得很多余,并不 RESTFUL
    Symo
        11
    Symo  
       2021-07-27 10:45:24 +08:00
    我觉得至少 GET POST 语义上一定要区分, 但是后端上只是把本应给 uri 做的正则匹配, 变成了对 json decode 之后的某字段的匹配来区分业务, 性能可能略微受影响?
    securityCoding
        12
    securityCoding  
       2021-07-27 11:05:43 +08:00 via Android
    你可以理解为命令模式,现在基本不这样用了
    lanlanye
        13
    lanlanye  
       2021-07-27 11:24:08 +08:00
    好处是没有文档你根本不知道这个接口怎么用,坏处也是没有文档你根本不知道这个接口怎么用……
    RESTful 应该也不会写 /create /update 之类的东西
    lux182
        14
    lux182  
       2021-07-27 11:32:58 +08:00
    网关路由,限流等不太好做。其他的其实挺好的
    learningman
        15
    learningman  
       2021-07-27 11:37:52 +08:00
    GraphQL, 请
    bthulu
        16
    bthulu  
       2021-07-27 11:44:53 +08:00   ❤️ 1
    批量操作你的 create/update 接口就不能接收一个数组吗?
    把所有接口统一到一个接口, 你文档怎么写?
    按你这么想, java 要这么多类, 这么多方法干嘛呢, 就一个 main 类, 一个 main 方法, 方法里传不同的参数不就行了吗? jdkAPI 加起来也就几万到几十万吧, 用一个 int 作为第一个入参, 就可以区分几亿个操作了, 够 jdk 用的了. 以后大家的代码就是这样的, 是不是很简单明了, 一看就懂?
    ```
    Main.main(1, xxxx);
    Main.main(16515, xxxx);
    Main.main(568, xxxx);
    Main.main(35656, xxxx);
    Main.main(956, xxxx);
    ...
    ```
    harde
        17
    harde  
       2021-07-27 11:46:04 +08:00
    是我精神恍惚了么?早年 Servlet 不就这样么。。。 后来 RESTful 盛行,才慢慢没人这么干了
    Macolor21
        18
    Macolor21  
       2021-07-27 11:54:34 +08:00
    @harde 一部分 java 开发除了大学学了 Servlet 之后,就再没接触过,大多都是基于 Spring 框架封装的东西在开发,早都忘了。另一部分可能压根没深入了解 Serlvet
    no1xsyzy
        19
    no1xsyzy  
       2021-07-27 11:56:37 +08:00
    在说什么
    这不就是一个变形了的 JSONRPC ?

    状态模式和日志模式的代价难说,如果编辑是稀疏的,那显然日志模式代价小得多。
    奇怪的优势是:日志模式可以减少需要解决的冲突。
    apifox
        20
    apifox  
       2021-07-27 12:31:20 +08:00
    用 GraphQL 不香吗?
    gfreezy
        21
    gfreezy  
       2021-07-27 12:31:21 +08:00
    这看起来像是 rpc 框架生成的。实际写的可能是类似 gprc 的 proto 文件,只是传输协议用的 json
    Building
        22
    Building  
       2021-07-27 12:34:41 +08:00 via iPhone
    一个本地路由。
    一个服务器路由。
    Building
        23
    Building  
       2021-07-27 12:39:59 +08:00 via iPhone
    以前用 php 生成模版的时候,路由就已经绑定在按钮上了,现在很多项目都不用后端生成 UI 了,直接 js 获取数据再由前端渲染 UI,这样后端就不需要再绑定路由了,所以一个入口就可以。
    harryhao
        24
    harryhao  
       2021-07-27 13:04:15 +08:00
    设置不同接口方便路由,比如不同接口的请求量、认证安全级别可能不同
    whajcf
        25
    whajcf  
       2021-07-27 13:33:43 +08:00
    这个好眼熟.. 然后我想到个我目前在用的一个场景: IoT 通信协议的编解码,报文根据不同的功能码携带对应的指令数据...
    waibunleung
        26
    waibunleung  
       2021-07-27 13:40:43 +08:00
    我感觉有点像 redis 的 pipeline 模式,管道式执行请求逻辑,一定程度上减少了接口的请求量,还能臆想到的就是能根据 command 来判断哪些 command 是可以异步 /并发执行的,哪些需要同步执行
    jmyz0455
        27
    jmyz0455  
       2021-07-27 13:50:58 +08:00
    这种设计我还真没见过。
    wolfie
        28
    wolfie  
       2021-07-27 13:51:57 +08:00
    wolai 之前抄袭道歉后 又上线了吗。
    weichengwu
        29
    weichengwu  
       2021-07-27 14:03:14 +08:00   ❤️ 1
    @wolfie #28 wolai 从来没道歉过吧?记得之前有个叫什么舟的抄袭道歉下架了
    bz5314520
        30
    bz5314520  
       2021-07-27 14:24:31 +08:00
    粗接口可以减少网络往返 ,接口设计粒度的太细就会导致业务逻辑散布到客户端,业务流交给了客户端控制。而且这也可能导致业务域不一致 比如一个上传文章 ,文章标签是必须的 ,你却把 api 拆成 上传文章再附加标签。具体如何抉择看业务,也是自身工程实践考验。软件哲学~~
    111111111111
        31
    111111111111  
       2021-07-27 15:05:05 +08:00
    很自然的想到了 GraphQL,JSONRPC 啊什么的。。
    karloku
        32
    karloku  
       2021-07-27 16:33:16 +08:00
    api 的 uri 能拆就拆, accesslog 的收集分析现成工具多, 好统计好分析
    用这种单 endpoint 的 rpc 风格就得自己把监控收集分析都定制一套.
    好处大概是比较容易配置新的行为模板, 方便在客户端那边生成按钮.
    charlie21
        33
    charlie21  
       2021-07-27 19:04:55 +08:00
    web 开发中,有没有后端完全作为接口提供数据,转发请求等操作由前端 html+js 实现的例子
    https://www.zhihu.com/question/21460500
    2013 年发的帖子,那时候把它称为 重前端应用
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1245 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 18:22 · PVG 02:22 · LAX 10:22 · JFK 13:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.