RESTful还是JSON-RPC?

最近突然被问到:“现在用的都是RESTful的接口吗?”一下子被问得有点蒙, 我确实在很多开发文档里见过REST、RESTful等等字眼,但我自己在用的却好像并不是RESTful的API。那我在用的是什么?与RESTful孰好孰坏?

我们在说RESTful和JSON-RPC的时候在讨论什么

首先需要明确的是,RESTful和JSON-RPC都不是指软件库、框架之类的东西,也不是设计模式(与MVC模式等无关),只是一种代码风格

这两个都属于Web Service模型,用于帮助人们解决应用程序与服务器传递数据的问题,详细细分大致如下:

  • SOA模型(面向消息)
  • RPC模型(面向方法)
    • XML-RPC
    • JSON-RPC
    • SOAP+WSDL
  • REST模型(面向资源)

当前而言,比较实际的基本只剩下JSON-RPC与REST两个比较流行的做法。

下面详细讲一下RESTful和JSON-RPC分别是什么,以及涉及到的一些知识点。

RESTful和JSON-PRC是什么

RESTful

REST,即Representational State Transfer。总结来说,就是

  1. 每一个URI代表一种资源
  2. 客户端和服务器之间,传递这种资源的某种表现层
  3. 客户端通过四个HTTP动词,对服务器端资源进行操作,实现”表现层状态转化”

这也是区别于RPC的面向方法,REST是面向资源的。

详细的内容可以参考这两篇文章:理解RESTful架构RESTful API 设计指南

举例(来源 WEB开发中,使用JSON-RPC好,还是RESTful API好? - 邵励治的回答 - 知乎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
作者:邵励治
链接:https://www.zhihu.com/question/28570307/answer/549218509
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

API : Create News
URL :
https://xxx.xxx.xxx.xxx/api/v2/news
METHOD :
POST
PARAMETERS :
name : String
content : String
RETURN :
STATUS : 200 OK
JSON :
{
"newsId" : "xxxxxx",
"name" : "xxxx",
"content" : "xxxx"
}

API : Get Single News
URL :
https://xxx.xxx.xxx.xxx/api/v2/news/:newsId
METHOD :
GET
RETURN :
STATUS : 200 OK
JSON :
{
"newsId" : "xxxxxx",
"name" : "xxxx",
"content" : "xxxx"
}

API : Edit News
URL :
https://xxx.xxx.xxx.xxx/api/v2/news/:newsId
METHOD :
PATCH
PARAMETERS :
name : String
content : String
RETURN :
STATUS : 200 OK
JSON :
{
"newsId" : "xxxxxx",
"name" : "xxxx",
"content" : "xxxx"
}

API : List News
URL :
https://xxx.xxx.xxx.xxx/api/v2/news
METHOD :
GET
RETURN :
STATUS : 200 OK
JSON :
[
{
"newsId" : "xxxxxx",
"name" : "xxxx",
"content" : "xxxx"
},
{
"newsId" : "xxxxxx",
"name" : "xxxx",
"content" : "xxxx"
},
......(省略)
]

比较简单地说就是,大家请求一样的url(GET方法有一个例外,url中带了一个id),通过不同的请求方法,分别进行不同的操作(CRUD)。

JSON-RPC

JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)传送协议,通过JSON传递内容。远程过程调用意思就是,用函数思维写API,用JSON传值,返回一个JSON。

同样举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
API : getNewsName
URL :
https://xxx.xxx.xxx.xxx/api/v1/getNewsName
METHOD :
POST
PARAMETERS :
newsId : String
RETURN :
{
"username" : "shaolizhi"
}

作者:邵励治
链接:https://www.zhihu.com/question/28570307/answer/549218509
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

等同于

1
getNewsName( newsId : String ) : String

该用哪种?

写这篇文章前,我看了知乎上这样一个问题WEB开发中,使用JSON-RPC好,还是RESTful API好?,可能有这么一个小规律:前端喜欢REST,后端喜欢JSON-RPC。

下面有一些在看了一些博客、回答以及结合自身开发经验的见解,如有谬误,欢迎指正。

RESTful的逻辑思维比较难转换

初学coding,比较定式的是函数的思维,考虑的是输入什么、输出什么。而RESTful面向资源的思维在写一些功能的时候就会比较别扭,考虑以下例子:写一个根据uid获取用户信息的接口。

  • JSON-RPC:输入用户的uid,输出用户的信息
  • REST:先用POST方法创建一个资源(查询),再GET请求一个资源(刚才创建的查询)

相比之下,REST的一个问题就暴露出来:一定要把所有东西都变成资源,然后再要把对应的操作映射成“增删改查”(HTTP的四个方法)。有时候一些很简单的功能,例如一个登录的功能,使用RESTful的话就会演变成十分复杂的问题,化简为繁没必要。

RESTful 在业务简单、有大量静态资源的情况下有优势

在研究过程中我看到了许多例子,都是用的博客、新闻之类的网站应用举的例子,实际上这些比较共同的特点就是里面有比较多的静态资源,而且业务逻辑相对比较简单,可以用简单的CRUD就能搞定了。那么优势在哪里呢?

例如获取某一篇文章的接口,因为请求某个资源,所以这个接口应该会使用GET方法,在url中直接带上文章的id来获取文章。

这里就需要引入GET和POST的异同了。

GET POST
后退按钮/刷新 无害 数据会被重新提交(浏览器应该告知用户数据会被重新提交)。
书签 可收藏为书签 不可收藏为书签
缓存 能被缓存 不能缓存
编码类型 application/x-www-form-urlencoded application/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多重编码。
历史 参数保留在浏览器历史中。 参数不会保存在浏览器历史中。
对数据长度的限制 是的。当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。 无限制。
对数据类型的限制 只允许 ASCII 字符。 没有限制。也允许二进制数据。
安全性 与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或其他敏感信息时绝不要使用 GET ! POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。
可见性 数据在 URL 中对所有人都是可见的。 数据不会显示在 URL 中。

比较显而易见的就是,GET请求可以被缓存。意思是,如果有一个人向web服务器GET请求,那么这个请求会被缓存起来,下一个同样的GET的时候,就会从缓存中取出上一次的返回,在高并发场景下缓存的引入能够有效提高性能。如果使用POST JSON的方法,每次web服务器都需要去调用底下的PHP或者Java之类,会增大开销。

另外一个比较细微的地方是,POST实际需要两次TCP,而GET只需要一次。尽管实践证明两种方法在网络良好时差距不大,但或多或少是一个优化的点。

然而考虑另外一个场景:搜索一篇文章。

首先这里缓存的意义就没有那么大了,每个用户输入的关键词可能都会不一样,之前的缓存优势没有了。然后,如果搜索中有多个条件,有可能会超出URL的上限(URL最大长度2048),还可能因为搜索条件中有中文等特殊字符出现编码等问题,且搜索中不能包含敏感信息之类,比较麻烦。可能有人会说,GET也可以在Body里面放JSON呀!标准来说GET应该是没有Body的,对比RESTful追求的“规范”有一些打脸。另外也没有办法解决上限的问题,可以说POST一个JSON可以一劳永逸

RESTful在规范、文档编写等比较方便

说实话这个也是见仁见智。在前后端分离开发里,接口文档、接口的结构本来就需要前后端开发比较积极地商量沟通。相对而言,RESTful比较“前端主导”,RPC比较“后端主导”。也有说法是RESTful对外,RPC对内的(这里我对内外的理解是,外指团队、公司外,内指团队内、前后端间)。

但是RESTful更规范化是肯定的,可以参考RESTful API 设计指南,且有现成的工具可以生成API文档。而JSON-RPC则比较宽松,具体的结构可以有组织、公司的规范,也会有个人的习惯、喜好。

然而在实际开发中我一直使用的JSON-RPC在前后端沟通、文档编写中都比较顺利。这里顺带安利一个API文档工具DOCWAY(以前叫小幺鸡),支持团队协作,简单好用。

GET?POST?

这个是研究这两个方式的时候衍生出的另一个问题:什么时候用GET?什么时候用POST?

毫无疑问,GET适合获取静态资源,通过QueryString获取参数;而传递JSON、包含敏感信息等应该用POST。但再来看两个我觉得比较经典的、微信开发文档的例子:

getAccessToken

这是一个获取AccessToken的接口,这里就使用了GET的方法,我觉得原因有以下几个

  1. 传递的都是ASCII字符,不受GET方法的限制
  2. appid和secret已经是相对乱码,也没有加密的必要
  3. 接口的功能比较简单,参数固定
  4. 请求的频率会比较高(考虑每次操作都需要这个token,且token比较容易过期)

再看另一个发送消息的接口。

subscribeMessage.send

这个接口就使用了POST的方法(尽管也有一个参数在url中)。分析如下

  1. 需要传递的内容较多,且可能有中文、特殊字符等,可能会超出GET的长度和字符限制
  2. 这里access_token没有放在json中,而是放在了url里。从功能上看这个放在json和url里都没有问题,可能是为了方便开发,服务端可以直接把前端送过来的json数据直接再传递给微信接口
  3. 接口结构比较复杂,而且多变。发送的消息根据模板可以有不一样的结构(每个模板的可以填的空、空的类型之类都不一样)
  4. 请求的频率比较低(不一定,不好做判断)

通过这个例子应该比较好理解。

到底咋办?

最近RESTful真的非常流行,但不要觉得我不用RESTful我就OUT了,我做的这个就不好。两者没有高下之分,只是两种不同的convention,习惯哪个就用哪个就好了,没必要盲目跟风非要用RESTful,又没理解透,最后做出来一个混搭产物REFU(Remove Extension From Url)。

我个人的习惯是用JSON-RPC(现在才知道的词,用了很久都不知道自己的做法叫什么,有点不好意思)。但是RESTful可以有一点参考作用,在做一些静态资源的功能的时候可以考虑使用GET搭配RESTful-like的API作为优化。

另外实际上使用REST的方法也比较简单,在Springboot中,只需要加上@RestController和修改@RequestMapping即可;在thinkphp中可以通过修改路由的方式实现RESTful。

其他参考

WEB开发中,使用JSON-RPC好,还是RESTful API好? - 知乎

HTTP 方法:GET 对比 POST

GraphQL