最近在复刻一些产品做练手,发现了不少新产品的 web api 接口很有意思。两个典型参考对象是 wolai.com 和 todoist.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 对不上,死活不能同步数据。)
就想问问各位大佬,这种接口设计除了我自己臆想分析的特征,还有什么别的是我没考虑到的吗?
1
littlemcdull 2021-07-27 10:12:13 +08:00
我能想到的是这种接口挺适用增量更新的,比如,一段时间后服务端新增了若干条数据记录,有删除,有修改,有新增(比如在另外一个终端 iPad 上登录了同个帐号,进行了一些操作,然后又换了个 iPhone 设备同一个帐号,需要从服务端同步数据)
|
2
Morriaty 2021-07-27 10:12:16 +08:00
es 的增删改接口 bulk 也是这个设计方式,和传统的 restful api 有啥优劣,我还真感受不出来,都一样🤣
|
3
javalaw2010 2021-07-27 10:15:01 +08:00
我个人感觉第二种接口会在应用有离线需求、多端同步的时候使用,比如笔记应用新增一篇文章,你肯定不能因为没有网络而不让用户新增文章,多端同步的时候,A 设备上的操作可能只有部分操作同步到了 B 设备,等到有条件的时候再把剩余的操作同步到 B 设备上,这种情况下第二种的方案会比较方便些。
|
4
sudoy 2021-07-27 10:15:25 +08:00
只能说这操作太骚了,随着项目越来越庞大,这个看起来就很费劲了,不管前后端,维护性和可拓展性就慢慢变差了
|
5
zjsxwc 2021-07-27 10:15:49 +08:00
方便一次接口请求,就能完成原先 restful 方式需要 N 次请求的场景。
|
6
JerryCha 2021-07-27 10:24:25 +08:00
可能它只是个 gayway,真接口隐藏在后面
|
7
ibegyourpardon OP @littlemcdull 其实这个模式我想过,颇有点类似 MySQL 了。
要实现同步数据,一个是传统的模式,拿最终数据。 二个是像这种模式一样,拿所有的操作行为日志,再完整走一次,就可以追回最终数据了。 但我想了下,这种前后端的场景中,走日志的模式代价远比直接获取最终数据高。 |
8
ibegyourpardon OP @javalaw2010 对对对,这个问题我也想过。
我甚至联想到了游戏服务器上,多玩家联网操作的逻辑同步。 但引申来了一个新问题。 我不同端操作先后有别,那我为了保证某种程度的最终一致性,后端除了记录数据,是不是还得记录下来自多个客户端的所有操作,类似 binlog 日志那样。 感觉又增加了存储队列上的额外操作。 |
9
dream4ever 2021-07-27 10:34:13 +08:00 2
@JerryCha gayway ? gateway ?
|
10
intmax2147483647 2021-07-27 10:38:08 +08:00 1
用后者不如用 GraphQL (也有些区别),用前者不如用 http method 区分不同的接口,写个 /create /get 在 URL 里面显得很多余,并不 RESTFUL
|
11
Symo 2021-07-27 10:45:24 +08:00
我觉得至少 GET POST 语义上一定要区分, 但是后端上只是把本应给 uri 做的正则匹配, 变成了对 json decode 之后的某字段的匹配来区分业务, 性能可能略微受影响?
|
12
securityCoding 2021-07-27 11:05:43 +08:00 via Android
你可以理解为命令模式,现在基本不这样用了
|
13
lanlanye 2021-07-27 11:24:08 +08:00
好处是没有文档你根本不知道这个接口怎么用,坏处也是没有文档你根本不知道这个接口怎么用……
RESTful 应该也不会写 /create /update 之类的东西 |
14
lux182 2021-07-27 11:32:58 +08:00
网关路由,限流等不太好做。其他的其实挺好的
|
15
learningman 2021-07-27 11:37:52 +08:00
GraphQL, 请
|
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); ... ``` |
17
harde 2021-07-27 11:46:04 +08:00
是我精神恍惚了么?早年 Servlet 不就这样么。。。 后来 RESTful 盛行,才慢慢没人这么干了
|
18
Macolor21 2021-07-27 11:54:34 +08:00
@harde 一部分 java 开发除了大学学了 Servlet 之后,就再没接触过,大多都是基于 Spring 框架封装的东西在开发,早都忘了。另一部分可能压根没深入了解 Serlvet
|
19
no1xsyzy 2021-07-27 11:56:37 +08:00
在说什么
这不就是一个变形了的 JSONRPC ? 状态模式和日志模式的代价难说,如果编辑是稀疏的,那显然日志模式代价小得多。 奇怪的优势是:日志模式可以减少需要解决的冲突。 |
20
apifox 2021-07-27 12:31:20 +08:00
用 GraphQL 不香吗?
|
21
gfreezy 2021-07-27 12:31:21 +08:00
这看起来像是 rpc 框架生成的。实际写的可能是类似 gprc 的 proto 文件,只是传输协议用的 json
|
22
Building 2021-07-27 12:34:41 +08:00 via iPhone
一个本地路由。
一个服务器路由。 |
23
Building 2021-07-27 12:39:59 +08:00 via iPhone
以前用 php 生成模版的时候,路由就已经绑定在按钮上了,现在很多项目都不用后端生成 UI 了,直接 js 获取数据再由前端渲染 UI,这样后端就不需要再绑定路由了,所以一个入口就可以。
|
24
harryhao 2021-07-27 13:04:15 +08:00
设置不同接口方便路由,比如不同接口的请求量、认证安全级别可能不同
|
25
whajcf 2021-07-27 13:33:43 +08:00
这个好眼熟.. 然后我想到个我目前在用的一个场景: IoT 通信协议的编解码,报文根据不同的功能码携带对应的指令数据...
|
26
waibunleung 2021-07-27 13:40:43 +08:00
我感觉有点像 redis 的 pipeline 模式,管道式执行请求逻辑,一定程度上减少了接口的请求量,还能臆想到的就是能根据 command 来判断哪些 command 是可以异步 /并发执行的,哪些需要同步执行
|
27
jmyz0455 2021-07-27 13:50:58 +08:00
这种设计我还真没见过。
|
28
wolfie 2021-07-27 13:51:57 +08:00
wolai 之前抄袭道歉后 又上线了吗。
|
29
weichengwu 2021-07-27 14:03:14 +08:00 1
@wolfie #28 wolai 从来没道歉过吧?记得之前有个叫什么舟的抄袭道歉下架了
|
30
bz5314520 2021-07-27 14:24:31 +08:00
粗接口可以减少网络往返 ,接口设计粒度的太细就会导致业务逻辑散布到客户端,业务流交给了客户端控制。而且这也可能导致业务域不一致 比如一个上传文章 ,文章标签是必须的 ,你却把 api 拆成 上传文章再附加标签。具体如何抉择看业务,也是自身工程实践考验。软件哲学~~
|
31
111111111111 2021-07-27 15:05:05 +08:00
很自然的想到了 GraphQL,JSONRPC 啊什么的。。
|
32
karloku 2021-07-27 16:33:16 +08:00
api 的 uri 能拆就拆, accesslog 的收集分析现成工具多, 好统计好分析
用这种单 endpoint 的 rpc 风格就得自己把监控收集分析都定制一套. 好处大概是比较容易配置新的行为模板, 方便在客户端那边生成按钮. |
33
charlie21 2021-07-27 19:04:55 +08:00
web 开发中,有没有后端完全作为接口提供数据,转发请求等操作由前端 html+js 实现的例子
https://www.zhihu.com/question/21460500 2013 年发的帖子,那时候把它称为 重前端应用 |