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

2021-07-27 10:01:34 +08:00
 ibegyourpardon

最近在复刻一些产品做练手,发现了不少新产品的 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 对不上,死活不能同步数据。)

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

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

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

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

状态模式和日志模式的代价难说,如果编辑是稀疏的,那显然日志模式代价小得多。
奇怪的优势是:日志模式可以减少需要解决的冲突。
apifox
2021-07-27 12:31:20 +08:00
用 GraphQL 不香吗?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/791983

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX