在进行API接口设计时,不同的开发人员可能有不同的设计风格,风格迥异。
那是否存在一种统一的接口设计方式,被广大开发人员所接受呢?
答: 这就是被普遍采用的RESTful API设计风格。
1. Restful风格设计-关键点
1. URL路径
路径又称"终点"(endpoint),表示API的具体网址,每个网址代表一种资源(resource)。
(1)URL地址尽量使用名词,不使用动词。
举例来说,以下是不好的例子:
/getProducts /listOrders
对于一个简洁结构,应该始终用名词。
GET /products:将返回所有产品信息
POST /products:将新建产品信息
GET /products/4:将获取产品4
PUT /products/4:将更新产品4
(2)API中的名词应该使用复数,无论单个资源或者所有资源。
举例来说,获取产品的API可以这样定义:
获取单个产品:http://127.0.0.1:8080/products/1
获取所有产品: http://127.0.0.1:8080/products
2. 请求方式
访问同一个URL地址,采用不同的请求方式,代表要执行不同的操作。
常用的HTTP请求方式有下面四个:
请求方式 | 说明 |
GET | 获取资源数据(单个或多个) |
POST | 新增资源数据 |
PUT | 修改资源数据 |
DELETE | 删除资源数据 |
例如:
GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息
DELETE /zoos/ID:删除某个动物园
3. 过滤信息
过滤参数可以放在查询字符串中。
在访问API接口获取数据时,可能需要对数据进行过滤。
下面是一些常见的参数:
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
4. 响应数据
针对不同操作,服务器向用户返回的响应数据应该符合以下规范:
GET /collection:返回资源对象的列表数据。
GET /collection/resource:返回单个资源对象数据。
POST /collection:返回新创建的资源对象数据。
PUT /collection/resource:返回完整的资源对象数据。
DELETE /collection/resource:返回空。
5. 响应数据格式
服务器返回的响应数据格式,应该尽量使用JSON。
6. 响应状态码
服务器向客户端返回的状态码和提示信息,常见的状态码如下:
200 OK - [GET/PUT]:服务器成功返回用户请求的数据
201 CREATED - [POST]:用户新建数据成功。
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
2. Restful风格设计-其他
1. 域名
应该尽量将API部署在专用域名之下。
如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。
2. 版本
应该将API的版本号放入URL。
http://www.example.com/api/1.0/foo
http://www.example.com/api/1.1/foo
http://www.example.com/api/2.0/foo
另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。
3. 错误处理
如果状态码是4xx,服务器就应该向用户返回出错信息。
{
error: "<error message>"
}
4. 超媒体
RESTful API最好做到Hypermedia(即返回结果中提供链接,指向其他API方法),使得用户不查文档,也知道下一步应该做什么。
比如,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"
}
上面代码表示,服务器给出了提示信息,以及文档的网址。
3、REST接口开发的核心任务
分析一下上节的案例,可以发现,在开发REST API接口时,视图中做的最主要有三件事:
- 将请求的数据(如JSON格式)转换为模型类对象
- 操作数据库
- 将模型类对象转换为响应的数据(如JSON格式)
1. 序列化Serialization
在以上操作中,涉及到两个概念:序列化和反序列化。
1)序列化
将程序中的一个数据结构类型转换为其他格式(字典、JSON、XML等),例如将Django中的模型类对象转换为JSON字符串,这个转换过程我们称为序列化。
如:
queryset = BookInfo.objects.all()
book_list = []
# 序列化
for book in queryset:
book_list.append({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
return JsonResponse(book_list, safe=False)
2)反序列化
将其他格式(字典、JSON、XML等)转换为程序中的数据,例如将JSON字符串转换为Django中的模型类对象,这个过程我们称为反序列化。
如:
json_bytes = request.body
json_str = json_bytes.decode()
# 反序列化
book_dict = json.loads(json_str)
book = BookInfo.objects.create(
btitle=book_dict.get('btitle'),
bpub_date=book_dict.get('bpub_date')
)
通过上节课的例子可以看到,在开发REST API时,视图中要频繁的进行序列化与反序列化的操作。
注:维基百科中对于序列化的定义。
2. 总结
在开发REST API接口时,我们在视图中在做的最核心的事是:
- 将数据库数据序列化为前端所需要的格式,并返回;
- 将前端发送的数据反序列化为模型类对象,并保存到数据库中。