前言
欢迎关注驿外残香 | HC的博客
RESTful架构作为当前最流行的互联网架构,得到了越来越多的互联网公司的青睐。一个结构清晰,符合规范,易于拓展的架构原则能够使得团队的开发更加规范化,且作为一个API也更加容易被团队成员或者大众开发者使用。
内涵
REST:(Resource) REpresentational State Transfer
翻译为:表现层状态转化
@Ivony老师对此概括为:URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作。
-
Resource:一个资源就是网络上的一个实体,可以是一份文档、一张图片、一种服务等等,资源是实体的概念性映射,而不是实体本身;相当于图片是资源,而放在本机中确切有的一张图片则是资源实体。每一个URI对应一个网络资源,通过访问特定的URI来操作特定的资源。
-
Transfer:资源在网络中以某种表现形式进行状态转移
-
Repersentational:表现层,代表资源的某种表现形式,比如JSON,XML,JPEG等
-
State Transfer:状态转化又称转态,通过HTTP动词实现。
-
“互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在客户端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。”
-
tip:资源不仅仅与数据库表产生联系,并且也可以指代某项存储数据或者服务。比如用户认证授权时即是对Session资源进行操作,进行银行转账时可以视为对transaction进行操作。
原则
- Server提供的RESTful API中,URL只使用名词来指定资源(推荐使用复数),原则上不使用动词。
- 用HTTP协议里的动词来实现资源的CRUD
- GET:获取资源
- POST:新建资源
- PUT:更新资源(客户端提供改变后的资源)
- PATCH:更新资源(客户端提供改变的资源)
- DELETE:删除资源
- HEAD:获取资源的元数据(不常用)
- OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的(不常用)
- 有些客户端只能使用
GET
和POST
这两种方法,服务器必须接受POST
模拟其他三种方法。此时客户端发出的HTTP请求,要加上X-HTTP-Method-Override
属性覆盖POST
- 使用HTTP Status Code 传递Server的状态信息
- 保证HEAD和GET方法是安全的,不会对资源状态有所改变
- 资源的地址推荐采用嵌套结构
- 文档跟API一定同步修改,甚至需要先修改文档
解析URL
格式:协议://域名/版本/路径?过滤信息
- 协议:一般使用HTTPS协议
- 版本:应将API的版本号放入URL,为了能够兼容旧版本的API
- 路径:又叫终点,一般来说,路径所用的名词往往跟数据库的表格名对应
- 过滤信息:如果记录数量很多,API应该提供参数过滤返回结果。多级URL最好除了第一级其他级别都用查询字符串表达
- ?limit=10:指定返回记录的数量
- ?offset=10:指定返回记录的开始位置
- ?page=2&per_page=100:指定第几页,以及每页的记录数
- ?sortby=name&order=asc:指定返回结果按照哪个属性排序以及排序顺序
- ?animal_type_id=1:设置筛选条件
服务器返回信息
-
状态码(Status Code):服务器应向用户返回的状态码和提示信息
- 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
- 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
- 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
- 204 NO CONTENT - [DELETE]:用户删除数据成功。
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
- 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
- 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
- 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
- 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
- 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
- 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
- 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
-
错误处理(Error handling):如果状态码是4XX,应该向用户返回出错信息
{ "error":"Invalid API key" }
-
返回结果(Response):API返回的数据格式应该是一个JSON对象,并且发生错误时不要返回200状态码。针对不同操作,服务器向用户返回的结果应该符合以下规范
- GET /collection:返回资源对象的列表(数组)
- GET /collection/resource:返回单个资源对象
- POST /collection:返回新生成的资源对象
- PUT /collection/resource:返回完整的资源对象
- PATCH /collection/resource:返回完整的资源对象
- DELETE /collection/resource:返回一个空文档
Hypermedia API
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。
{ "link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" } }
上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。
Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。
{ "current_user_url": "https://api.github.com/user", "authorizations_url": "https://api.github.com/authorizations", // ... }
从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。
{ "message": "Requires authentication", "documentation_url": "https://developer.github.com/v3" }
上面代码表示,服务器给出了提示信息,以及文档的网址。
注意事项:
理解RESTful架构 --阮一峰一文中指出:
另一个设计误区,就是在URI中加入版本号。
因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。版本号可以在HTTP请求头信息的Accept字段中进行区分(参见Versioning REST Services):
RESTful API 最佳实践 --阮一峰一文则指出:
应该将API的版本号放入URL。
https://api.example.com/v1/
另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。
前者为2011年版本,后者为2014年版本。
可能是每个时间段的现状或理解不同而造成的差异。
且目前GitHub API已采用将API的版本号放入URL
故应以后者为准。
存在问题:
-
在复杂的系统上,复杂的业务要操作很多种资源的时候URL应该如何体现?当一个资源操作涉及多个数据库表的时候该如何设计URL?
如果有对该问题有思路的欢迎留言评论。