0%

究竟怎么理解restful设计风格?我喜欢这个比喻

REST – REpresentational State Transfer 直译:表现层状态转移。这是什么鬼???

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

举个例子,现在很多应用里面都支持有分享到微信,分享到QQ,分享到知乎这样的分享功能,这个功能的实现其实就是一种API,相当于腾讯,知乎已经将这个API封装好了,其他应用如果想要调用,直接调用即可。而restful就是这种API的设计模式。

REST – REpresentational State Transfer
首先,之所以晦涩是因为前面主语被去掉了,全称是 Resource Representational State Transfer:通俗来讲就是:资源在网络中以某种表现形式进行状态转移。分解开来:
Resource:资源,即数据(前面说过网络的核心)。比如 newsfeed,friends等;
Representational:某种表现形式,比如用JSON,XML,JPEG等;
State Transfer:状态变化。通过HTTP动词实现。

简单来讲,我们可以这么理解restful

URL定位资源,用HTTP动词(GET,POST,DELETE,DETC等)描述操作。
1、看url就知道要什么
2、看http method就知道干什么
3、看http status code就知道结果如何

其实开始了解restful的时候我是一脸懵逼的,不过现在稍微理解了,举个生动一点的例子吧

Level 0 - 面向前台


我们在咖啡店向前台点了一杯拿铁,这个过程可以用这段文字来描述:

1
2
3
4
5
{
"addOrder": {
"orderName": "latte"
}
}

我们通过这段文字,告诉前台,新增一笔订单,订单是一杯拿铁咖啡,接着,前台给我们返回这么一串回复:

1
2
3
{
"orderId": "1"
}

假设我们有一张会员卡,我们想查询一下这张会员卡的余额,这时候,要向前台发起另一个询问:

1
2
3
4
5
{
"queryBalance": {
"cardId": "123456"
}
}

查询卡号为447031335的卡的余额,查询的结果返回来了:

1
2
3
{
"balance": "0"
}

没钱……
哈哈,没钱,现在我们要跟前台说,这杯咖啡不要了:

1
2
3
4
5
{
"deleteOrder": {
"orderId": "1"
}
}

Level 1 - 面向资源


现在这家咖啡店越做越大,来喝咖啡的人越来越多,单靠前台显然是不行的,店主决定进行分工,每个资源都有专人负责,我们可以直接面向资源操作。
比如还是下单,请求的内容不变,但是我们多了一条消息:

1
2
3
4
5
6
//orders
{
"addOrder": {
"orderName": "latte"
}
}

多了一个斜杠和orders,这是什么意思?
这个表示我们这个请求是发给哪个资源的,订单是一种资源,我们可以理解为是咖啡厅专门管理订单的人,他可以帮我们处理所有有关订单的操作,包括新增订单、修改订单、取消订单等操作。
接着还是会返回订单的编号给我们:

1
2
3
{
"orderId": "1"
}

下面,我们还是要查询会员卡余额,这次请求的资源变成了cards:

1
2
3
4
5
6
//cards
{
"queryBalance": {
"cardId": "123456"
}
}

接下来是取消订单:

1
2
3
4
5
6
//orders
{
"deleteOrder": {
"orderId": "1"
}
}

Level2 - 打上标签


接下来,店主还想继续优化他的咖啡厅的服务流程,他发现负责处理订单的员工,每次都要去订单内容里面看是新增订单还是删除订单,还是其他的什么操作,十分不方便,于是规定,所有新增资源的请求,都在请求上面写上大大的‘POST’,表示这是一笔新增资源的请求。
其他种类的请求,比如查询类的,用‘GET’表示,删除类的,用‘DELETE’表示,修改用PATCH表示。
来,我们再来重复上面那个过程,来一杯拿铁:

1
2
3
4
POST /orders
{
"orderName": "latte"
}

请求的内容简洁多啦,不用告诉店员是addOrder,看到POST就知道是新增,返回的内容还是一样:

1
2
3
{
"orderId": "1"
}

接着是查询会员卡余额,这次也简化了很多:

1
2
3
4
GET /cards
{
"cardId": "123456"
}

这个请求我们还可以进一步优化为这样:
GET /cards/123456
直接把要查询的卡号写在后面了。
没错,接着,取消订单:
DELETE /orders/1

Level 3 - 完美服务


忽然有一天,有个顾客抱怨说,他买了咖啡后,不知道要怎么取消订单,咖啡厅一个店员回了一句,你不会看我们的宣传单吗,上面不写着:
DELETE /orders/{orderId}
顾客反问道,谁会去看那个啊,店员不服,又说到,你瞎了啊你……后面两人吵着吵着还打了起来… 
噗,真是悲剧…
有了这次教训,店长决定,顾客下了单之后,不仅给他们返回订单的编号,还给顾客返回所有可以对这个订单做的操作,比如告诉用户如何删除订单。现在,我们还是发出请求,请求内容和上一次一样:

1
2
3
4
POST /orders
{
"orderName": "latte"
}

但是这次返回时多了些内容:

1
2
3
4
5
6
7
{
"orderId": "1",
"link": {
"rel": "cancel",
"url": "/order/1"
}
}

这次返回时多了一项link信息,里面包含了一个rel属性和url属性,rel是relationship的意思,这里的关系是cancel,url则告诉你如何执行这个cancel操作,接着你就可以这样子来取消订单啦:
DELETE /orders/1
哈哈,这服务真是贴心,以后再也不用担心店员和顾客打起来了。
Level3的Restful API,给使用者带来了很大的遍历,使用者只需要知道如何获取资源的入口,之后的每个URI都可以通过请求获得,无法获得就说明无法执行那个请求。
现在绝大多数的RESTful接口都做到了Level2的层次,做到Level3的比较少。当然,这个模型并不是一种规范,只是用来理解Restful的工具。所以,做到了Level2,也就是面向资源和使用Http动词,就已经很Restful了。

Levels的意义

Level 1 解释了如何通过分治法(Divide and Conquer)来处理复杂问题,将一个大型的服务端点(Service Endpoint)分解成多个资源。
Level 2 引入了一套标准的动词,用来以相同的方式应对类似的场景,移除不要的变化。
Level 3 引入了可发现性(Discoverability),它可以使协议拥有自我描述(Self-documenting)的能力。
这一模型帮助我们思考我们想要提供的HTTP服务是何种类型的,同时也勾勒出人们和它进行交互时的期望。