在进行API接口设计时,不同的开发人员可能有不同的设计风格,风格迥异。
那是否存在一种统一的接口设计方式,被广大开发人员所接受呢?
答: 这就是被普遍采用的RESTful API设计风格。
路径又称"终点"(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
访问同一个URL地址,采用不同的请求方式,代表要执行不同的操作。
常用的HTTP请求方式有下面四个:
请求方式
说明
GET
获取资源数据(单个或多个)
POST
新增资源数据
PUT
修改资源数据
DELETE
删除资源数据
例如:
GET /zoos:列出所有动物园 POST /zoos:新建一个动物园(上传文件) GET /zoos/ID:获取某个指定动物园的信息 PUT /zoos/ID:更新某个指定动物园的信息 DELETE /zoos/ID:删除某个动物园过滤参数可以放在查询字符串中。
在访问API接口获取数据时,可能需要对数据进行过滤。
下面是一些常见的参数:
?limit=10:指定返回记录的数量 ?offset=10:指定返回记录的开始位置。 ?page=2&per_page=100:指定第几页,以及每页的记录数。 ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。针对不同操作,服务器向用户返回的响应数据应该符合以下规范:
GET /collection:返回资源对象的列表数据。 GET /collection/resource:返回单个资源对象数据。 POST /collection:返回新创建的资源对象数据。 PUT /collection/resource:返回完整的资源对象数据。 DELETE /collection/resource:返回空。服务器返回的响应数据格式,应该尽量使用JSON。
服务器向客户端返回的状态码和提示信息,常见的状态码如下:
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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。状态码的完全列表参见这里或这里。
应该尽量将API部署在专用域名之下。
https://api.example.com
如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。
https://www.example.com/api/
应该将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采用这种做法。
如果状态码是4xx,服务器就应该向用户返回出错信息。
{ error: "<error message>" }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" }上面代码表示,服务器给出了提示信息,以及文档的网址。
分析一下上节的案例,可以发现,在开发REST API接口时,视图中做的最主要有三件事:
将请求的数据(如JSON格式)转换为模型类对象操作数据库将模型类对象转换为响应的数据(如JSON格式)在以上操作中,涉及到两个概念:序列化和反序列化。
将程序中的一个数据结构类型转换为其他格式(字典、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)将其他格式(字典、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时,视图中要频繁的进行序列化与反序列化的操作。
注:维基百科中对于序列化的定义。
在开发REST API接口时,我们在视图中在做的最核心的事是:
将数据库数据序列化为前端所需要的格式,并返回;将前端发送的数据反序列化为模型类对象,并保存到数据库中。