Restful软件架构风格简述

前言

前后端分离已经是业界所共识的一种开发/部署模式了。前端和后端人员的分工越来越明确,他们之间如何协同工作变得越来越重要。这种模式下,我们通过API来解耦前端和后端开发过程。因此API是前后端必须共同理解的语言。双方只需要约定数据接口,而不必在代码层面有任何的耦合。预先确定一个双方都能够理解的编程风格是前后端分离非常重要的一环。

什么是restful编程风格

Restful软件架构风格简述_第1张图片
220px-Roy_Fielding.jpg

Fielding博士的 那篇经典论文( 中文版)对万维网架构的贡献可谓是居功至伟。可想而知,当REST一词变得流行起来之后,其滥用甚至是“挂羊头卖狗肉”的现象是不可避免的。而糟糕的是,对于那些没有时间、也没有耐心去仔细阅读该论文的人来说,可能就会在看过或用过某些号称具有REST风格的应用之后对REST本身产生错误的理解,进而在错误的思想指导之下错误地运用REST。这正是其创造者本身所不愿意看到的。

Wikipedia: 表征性状态传输(英文:Representational State Transfer,简称REST)是Roy Fielding博士于2000年在他的博士论文中提出来的一种软件架构风格。

Roy Fielding是HTTP协议(1.0版和1.1版)的主要设计者,事实上HTTP 1.1规范正是基于REST架构风格的指导原理来设计的。需要注意的是,REST是一种设计风格而不是标准,如果一个架构符合REST原则,我们就称它为RESTful架构。

为什么要写成restful风格


在「远古时代」前端后端是融合在一起的,比如之前的PHP,JSP,ASP等等。近年来随着移动互联网的飞速发展,各种类型的Client端层出不穷,就需要通过一套统一的接口分别为Web,iOS和Android乃至桌面端提供服务。另外对于广大平台来说,比如Facebook platform,微博开放平台,微信公共平台等,它们不需要有显式的前端,只需要一套提供服务的接口,于是RESTful更是它们最好的选择。

理解REST三要素

  • Resource:资源,即数据。比如newsfeed,friends,order等;
  • Representational:某种表现形式,比如用JSON,XML,JPEG等;
  • State Transfer:状态变化。通过HTTP动词实现。
Restful软件架构风格简述_第2张图片
20150117210140681.png

然后再来理解一个具体的RESTful架构——面向资源的架构(Resource-Oriented Architecture,ROA):
资源是由URI来指定。所谓「上网」,就是与互联网上一系列的「资源」互动,调用它的URI。
对资源的操作包括获取、创建、修改和删除资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。
通过操作资源的表现形式来操作资源。具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定。
资源的表现形式则是XML或者HTML,取决于读者是机器还是人,是消费web服务的客户软件还是web浏览器。当然也可以是任何其他的格式。

怎样做

应用于Web服务,符合REST设计风格的Web API称为RESTful API。它从以下三个方面资源进行定义:
直观简短的资源地址:URI,比如:http://example.com/resources/
;每一个URI代表一种资源;
传输的资源:Web服务接受与返回的互联网媒体类型,比如:JSON,XML,YAML等。
对资源的操作:Web服务在该资源上所支持的一系列请求方法(比如:POST,GET,PUT或DELETE)。

来个图,


Restful软件架构风格简述_第3张图片

Restful API设计要点

1.使用名词而不是动词

Resource
资源

GET读
POST创建
PUT修改
DELETE删除

/cars 返回 cars集合 创建新的资源 批量更新cars 删除所有cars
/cars/711 返回特定的car 该方法不允许(405) 更新一个指定的资源 擅长指定资源

不要使用:
/getAllCars
/createNewCar
/deleteAllRedCars

2.Get方法和查询参数不应该涉及状态改变

使用PUT, POST 和DELETE 方法 而不是 GET 方法来改变状态,不要使用GET 进行状态改变:

GET /users/711?activate
GET /users/711/activate

3.使用复数名词

不要混淆名词单数和复数,为了保持简单,只对所有资源使用复数。

/cars 而不是 /car
/users 而不是 /user
/products 而不是 /product
/settings 而部署 /setting

4. 使用子资源表达关系

如果一个资源与另外一个资源有关系,使用子资源:

GET /cars/711/drivers/ 返回 car 711的所有司机
GET /cars/711/drivers/4 返回 car 711的4号司机

5.使用Http头声明序列化格式

在客户端和服务端,双方都要知道通讯的格式,格式在HTTP-Header中指定

Content-Type 定义请求格式
Accept 定义系列可接受的响应格式

6.使用HATEOAS

Hypermedia as the Engine of Application State 超媒体作为应用状态的引擎,超文本链接可以建立更好的文本浏览:

{
"id": 711,
"manufacturer": "bmw",
"model": "X5",
"seats": 5,
"drivers": [
{
"id": "23",
"name": "Stefan Jauker",
"links": [
{
"rel": "self",
"href": "/api/v1/drivers/23"
}
]
}
]
}
注意href指向下一个URL

7.为集合提供过滤 排序 选择和分页等功能

Filtering过滤:

使用唯一的查询参数进行过滤:

GET /cars?color=red 返回红色的cars
GET /cars?seats<=2 返回小于两座位的cars集合

Sorting排序:

允许针对多个字段排序

GET /cars?sort=-manufactorer,+model

这是返回根据生产者降序和模型升序排列的car集合

Field selection

移动端能够显示其中一些字段,它们其实不需要一个资源的所有字段,给API消费者一个选择字段的能力,这会降低网络流量,提高API可用性。

GET /cars?fields=manufacturer,model,id,color

Paging分页

使用 limit 和offset.实现分页,缺省limit=20 和offset=0;

GET /cars?offset=10&limit=5

为了将总数发给客户端,使用订制的HTTP头: X-Total-Count.

链接到下一页或上一页可以在HTTP头的link规定,遵循Link规定:

Link: https://blog.mwaysolutions.com/sample/api/v1/cars?offset=15&limit=5; rel="next",
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=50&limit=3; rel="last",
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=0&limit=5; rel="first",
https://blog.mwaysolutions.com/sample/api/v1/cars?offset=5&limit=5; rel="prev",

8.版本化你的API

使得API版本变得强制性,不要发布无版本的API,使用简单数字,避免小数点如2.5.

一般在Url后面使用?v

/blog/api/v1

9. 使用Http状态码处理错误

如果你的API没有错误处理是很难的,只是返回500和出错堆栈不一定有用

Http状态码提供70个出错,我们只要使用10个左右:

200 – OK – 一切正常
201 – OK – 新的资源已经成功创建
204 – OK – 资源已经成功擅长

304 – Not Modified – 客户端使用缓存数据

400 – Bad Request – 请求无效,需要附加细节解释如 "JSON无效"
401 – Unauthorized – 请求需要用户验证
403 – Forbidden – 服务器已经理解了请求,但是拒绝服务或这种请求的访问是不允许的。
404 – Not found – 没有发现该资源
422 – Unprocessable Entity – 只有服务器不能处理实体时使用,比如图像不能被格式化,或者重要字段丢失。

500 – Internal Server Error – API开发者应该避免这种错误。

使用详细的错误包装错误:

{
"errors": [
{
"userMessage": "Sorry, the requested resource does not exist",
"internalMessage": "No car found in the database",
"code": 34,
"more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"
}
]
}

10.允许覆盖http方法

一些代理只支持POST 和 GET方法, 为了使用这些有限方法支持RESTful API,需要一种办法覆盖http原来的方法。

使用订制的HTTP头 X-HTTP-Method-Override 来覆盖POST 方法.

幂等性安全性

HTTP动词
HTTP并没有定义很多动词来描述web服务中可能出现的行为,它只用了一个标准动词集合来处理各种相似情况,从而让API变得更直观。每个动词通过两种属性的组合来满足不同的场景需求。
幂等性:操作可以被重复执行,就算在失败以后。

安全性:对客户端来说操作不会产生副作用。

GET --幂等、安全
用来从服务器端读取状态。这个操作是安全的,所以它可以被执行很多次而不会对数据有任何影响,也就是说执行它一次跟执行十次是一样的效果。从幂等性方面来看,多次请求跟单个请求总能得到相同的结果。
POST
一般用来在服务器端创建某种状态。这个操作不具备幂等性跟安全性,所以多次请求会在服务器端创建多个资源。因为POST是不幂等的, 所以不应该被用来做跟金钱有关系的操作,试想一次失败的请求如果被执行多次,那么很可能转账或者支付也被执行了多次。
PUT
虽然它也可以被用来创建状态,但主要还是用来在服务器端更新状态的。它是幂等的,但不安全,因为它会改变服务端的状态。因为它的幂等性,PUT可以被用来处理跟金钱有关系的操作。
DELETE
用来在服务器端删除状态。它也是幂等非安全的,因为它会移除服务端的状态。它之所以是幂等的,是因为重复删除一个状态的结果是一样。

是否一定要用REST

前面说了这么多REST,最后再冷静下来思考一下,是否一定要用REST,是否一定要严格遵循REST设计风格。
引用知乎作者淘李福的话
REST本身不是架构,只是一种架构风格,理解它的时候要参考这个架构风格出现的环境所施加的约束条件。
REST的目的是“建立十年内不会过时的软件系统架构",所以它具备三个特点:

  1. 状态无关 —— 确保系统的横向拓展能力
  2. 超文本驱动,Fielding的原话是”hypertext-driven" —— 确保系统的演化能力
  3. 对 resource 相关的模型建立统一的原语,例如:uri、http的method定义等 —— 确保系统能够接纳多样而又标准的客户端
    从另外一个角度看,第一条保证服务端演化,第三条保证客户端演化,第二条保证应用本身的演化,这实在是一个极具抽象能力的方案。

引用列表
https://en.wikipedia.org/wiki/Roy_Fielding
https://blog.jimmylv.info/2015-11-11-what-is-really-rest/
http://www.jdon.com/soa/10-best-practices-for-better-restful-api.html
https://www.oschina.net/translate/what-does-restful-really-mean

https://www.zhihu.com/question/33959971/answer/60280136

你可能感兴趣的:(Restful软件架构风格简述)