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。总结来说,就是
- 每一个URI代表一种资源
- 客户端和服务器之间,传递这种资源的某种表现层
- 客户端通过四个HTTP动词,对服务器端资源进行操作,实现”表现层状态转化”
这也是区别于RPC的面向方法,REST是面向资源的。
详细的内容可以参考这两篇文章:理解RESTful架构,RESTful API 设计指南
举例(来源 WEB开发中,使用JSON-RPC好,还是RESTful API好? - 邵励治的回答 - 知乎)
1 | 作者:邵励治 |
比较简单地说就是,大家请求一样的url(GET方法有一个例外,url中带了一个id),通过不同的请求方法,分别进行不同的操作(CRUD)。
JSON-RPC
JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)传送协议,通过JSON传递内容。远程过程调用意思就是,用函数思维写API,用JSON传值,返回一个JSON。
同样举例
1 | API : getNewsName |
等同于
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。但再来看两个我觉得比较经典的、微信开发文档的例子:
这是一个获取AccessToken的接口,这里就使用了GET的方法,我觉得原因有以下几个
- 传递的都是ASCII字符,不受GET方法的限制
- appid和secret已经是相对乱码,也没有加密的必要
- 接口的功能比较简单,参数固定
- 请求的频率会比较高(考虑每次操作都需要这个token,且token比较容易过期)
再看另一个发送消息的接口。
这个接口就使用了POST的方法(尽管也有一个参数在url中)。分析如下
- 需要传递的内容较多,且可能有中文、特殊字符等,可能会超出GET的长度和字符限制
- 这里access_token没有放在json中,而是放在了url里。从功能上看这个放在json和url里都没有问题,可能是为了方便开发,服务端可以直接把前端送过来的json数据直接再传递给微信接口
- 接口结构比较复杂,而且多变。发送的消息根据模板可以有不一样的结构(每个模板的可以填的空、空的类型之类都不一样)
- 请求的频率比较低(不一定,不好做判断)
通过这个例子应该比较好理解。
到底咋办?
最近RESTful真的非常流行,但不要觉得我不用RESTful我就OUT了,我做的这个就不好。两者没有高下之分,只是两种不同的convention,习惯哪个就用哪个就好了,没必要盲目跟风非要用RESTful,又没理解透,最后做出来一个混搭产物REFU(Remove Extension From Url)。
我个人的习惯是用JSON-RPC(现在才知道的词,用了很久都不知道自己的做法叫什么,有点不好意思)。但是RESTful可以有一点参考作用,在做一些静态资源的功能的时候可以考虑使用GET搭配RESTful-like的API作为优化。
另外实际上使用REST的方法也比较简单,在Springboot中,只需要加上@RestController
和修改@RequestMapping
即可;在thinkphp中可以通过修改路由的方式实现RESTful。