Restful API设计指南
这篇博文讲前后端交互的Restful API设计考虑。API一定是基于HTTP协议并尽量靠拢Restful规范设计的。如果不了解HTTP协议,可以参考我的博文HTTP必知必会。关于Restful是怎么回事,可以考虑先参考阮一峰的博文理解RESTful架构;嫌麻烦的可以略过直接阅读我的这篇博文。
基于资源的API设计
所谓资源,是一种对事物的抽象。一个人、一头牛、一张电影票、一句话、一个想法都可以抽象为资源。
资源的URL表示
URL是资源的定位,是一种映射关系,即一个URL一定定位到最多不超过一个的资源。根据URL布局的特点,分为三种类型的资源:
-
集合资源
对于形如
/users的资源称为集合资源,它表示一群用户。 -
个体资源
对于形如
/users/u123的资源称为个体资源,它表示一个用户,它是通过一个唯一标识的id(即u123)来确定这个用户。 -
子集合资源
对于形如
/users/u123/messages的资源称为子集合资源,它表示某个用户的一群消息,这个用户是通过唯一标识的id(即u123)来确定的。
补充:关于id
资源的id对于同种类型的资源来说一定是唯一的。也就是说,确定一个个体资源只需要通过下面的方式即可
/messages/m123
而不必
/users/u123/messages/m123
资源的表述
资源本身是一种抽象,它必须要转化为一种机器可读的形式才能进行传输和处理。表述方式的例子如表单形式、XML、JSON等。可以通过HTTP头Content-Type来指定资源的表述形式,常见的例子:
- 纯文本:text/plain
- HTML文档:text/html
- 表单格式:application/x-www-form-urlencoded
- XML文档:text/xml
- JSON文档:application/json
这里的设计中只考虑一种表述形式,即JSON。希望这一点不会给API的调用带来困扰。
资源的操作
基本的增删改查
对于的基本的增删改查操作,除了定位到资源以外,只需要附加一个HTTP方法够了。根据Restful规范,方法对于集合的意义一般是:
- POST:创建
- GET:查询
- PUT:完整更新
- PATCH:部分更新
- DELETE:删除
这些方法适用于不同的URL类型,总结如下:
-
GET+(子)集合资源
- GET /users 返回用户集合
- GET /users/u123/messages 返回指定用户的消息集合
-
POST+(子)集合资源
- POST /users 创建一个新用户
- POST /users/u123/messages 为指定用户创建一个新消息
-
GET+个体资源
- GET /users/u123 返回指定用户
-
PUT/PATCH+个体资源
- PUT /users/u123 更新指定用户(完整)
- PATCH /users/u123 更新指定用户(部分)
-
DELETE+个体资源
- DELETE /users/u123 删除指定用户
其他的动作
当对资源的操作跳出了基本的增删改查范畴,就在资源的URL表示之后增加动作的名称即可。这可以通过两个典型的例子来阐述。
例子一:用户登录
这个属于对集合资源进行的操作。其接口表示可设定为:
- POST /users/login
只需在集合资源的URL表示(即/users)之后附加动作名(即/login)即可。在这种情况下,POST是典型方法。
例子二:书籍标星和去标
这是一对接口,其中一个是对一本书进行标星操作,另外一个是对其进行取消标星操作。这两个接口都属于对个体资源的操作。其接口表示可分别设定为:
- POST /books/b123/star
- DELETE /books/b123/star
首先,需要在个体资源的URL表示(即/books/b123)之后附加动作名(即/star)。其次,这里用到对立的两个方法POST和DELETE。
例子三:查询的count
一个查询接口会返回一个资源数组,但有时我们需要知道满足某个查询条件的资源总数。这个在集合资源的URL之后附加动作count即可。例如:
- GET /books/count 返回书籍资源总数
- GET /users/count?age=18 返回年龄18岁的用户资源总数
注意,这时分页控制无效。
操作的参数
在参数设计里,一个基本的设计原则是要保证简洁,避免嵌套过深。参数的格式可以根据实际情况限定为几种或不限格式。不过,至少保证表单格式和JSON格式这两种格式的支持。其中JSON格式支持任意的嵌套;表单格式对嵌套的支持不理想。
参数分为一般参数和控制参数。从形式上看,控制参数以下划线开头。这么设计是为了与一般参数进行区分。一般参数例如name、age等,控制参数例如_sort等。
通用参数总结
筛选
这里的筛选只支持完全匹配,适用于GET集合资源的接口。筛选的方式是简单直接的,直接是字段名=值的形式即可。例如下面的接口返回年龄为18的所有男性用户:
GET /users?age=18&gender=male
排序(_sort)
排序参数_sort是逗号分隔的字段列表,以+/-表示升/降序。
- age: age升序
- +age: age升序,同上
- -age: age降序
- -age,name: 先以age降序,age相等的以name升序
分页(_range)
分页参数_range的构建也是直接的,以下划线分隔它的起始和结束资源的序号。资源计数从1开始,这点要尤其注意。
- 10_100 : 返回第10个到第100个资源集合
- _100 : 返回第1个到第100个集合资源
- 1_ : 返回第1个到最后一个集合资源
- _ : 返回所有的集合资源
内嵌(_embed)
说清楚内嵌的用法,先定义一个资源Book:
{
id: 'b123',
title: 'xxx',
author: {
id: 'u123',
name: 'yyy'
}
}
通过GET接口获取的资源表述中,对于内嵌的资源默认只会返回其id,而不会返回其完整表述。例如调用GET /books/b123返回的数据是:
{
id: 'b123',
title: 'xxx',
author: {
id: 'u123'
}
}
这个时候,想要获取完整的author资源,需要再调用GET /users/u123。
然而,通过_embed参数,可以一步就直接返回包含完整User资源的author链接,就像我们一开始定义的那样。调用GET /users/u123?\_embed=author,返回以下的结果:
{
id: 'b123',
title: 'xxx',
author: {
id: 'u123',
name: 'yyy'
}
}
多个内嵌资源用逗号分隔即可。
操作的返回值
操作的返回内容,它的表述格式是可选的。只限定为JSON格式是一种可选的简洁方案。
基于操作类型的参数和返回值模式
不同的操作类型,其参数和返回值模式是有一定规律性的。现总结如下:
GET+(子)集合资源
这个操作请求的是一个资源集合,返回的是一个JSON数组。
其参数能包括:筛选、排序、分页、内嵌。
GET+个体资源
这个操作请求的是一个个体资源,返回的是一个JSON对象。
其参数只包括:内嵌。
POST+(子)集合资源
这个操作请求创建一个新资源,返回的是一个JSON对象,表述新创建的资源。
其参数就是该资源的表述。
PUT/PATCH+个体资源
这个操作请求更新一个旧资源,返回的是一个JSON对象,表述更新后的资源。
其参数就是该资源的表述。
DELETE+个体资源
这个操作请求删除一个资源,无返回值,无参数。
其他操作
其他操作视情况而定,一般无返回值,无参数。
操作的错误
任何操作都会发生出错情况。出错的原因有很多种,有源于客户端的,也有源于服务器的。有预料中的,也有预料之外的。对于出错情况,服务器要向客户端发送一个状态码及附加一个JSON格式的错误消息(这里像返回值的考虑一样也只考虑JSON格式了)。一个例子就是:
401 Unauthorized
{
message: "没登录"
}
这里的message字段作为错误状态的描述,作为解释出错的原因。如果表述的合适,可以作为最终用户的提示信息,直接在弹框中显示。
这里面没有考虑加入更多的字段了,比如一个code字段来更细致的区别错误的类型。这种考虑是基于客户端对于接口调用出错无能为力,一般能做的就两种:
- 显示错误信息;如果是幂等的接口,(可选地)重传。
- 如果是授权相关错误,跳转到登录逻辑并可选地重传。
补充
验证信息
关于用户的授权在HTTP和Restful中的方案,市面上有很多种。这里介绍的是一种Token机制。很简单,用户通过某个接口(例如/users/login)获取授权的Token,然后每次调用接口时将这个Token附加到Header Access-Token中。
_token伪id
用户的Token不仅用于授权,也用于标识用户的身份。当需要传递用户id的时候,往往发现用户的id信息可以从Token中获取。举个例子,id为u123的用户调用如下接口时:
GET /users/u123
Access-Token: 包含u123的授权信息
其中Token信息可以从相应的Header中获取,所以此时明确标明id u123没有必要。此时可以用一个占位符()占位一下即可。
GET /users/_token
Access-Token: 包含u123的授权信息

浙公网安备 33010602011771号