RESTful规范
引子:在正式开始学习restful规范之前,我们先回顾一下自己前面写的路由,比如以图书管理系统为列,在没有学习RESTful规范之前,我们书写的路由是下面的这种方式
url(r'^show_author/',views.show_author),
url(r'^show_book/',views.show_book),
url(r'^show_publish/',views.show_publish),
url(r'^add_author/',views.add_author),
url(r'^delete/',views.delete),
url(r'^edit_author/',views.edit_author),
url(r'^add_book',views.add_book),
url(r'^edit_book',views.edit_book),
url(r'^add_publish',views.add_publish),
url(r'^edit_publish',views.edit_publish),
这种方式我们自己看是没有任何问题的,但是后面再遇到前后端分离的时候,我们总不能让前端小美女一直来问我们吧,毕竟大家时间都很宝贵,而且这种方式书写的路由,如果让别的心怀不轨的人一看就知道你做了什么,在路由中你暴露了太多你的数据信息,为了避免上面所说的所有的麻烦,我们就统一使用了RESTful规范。
什么是RESTful规范?
RESTful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
RESTful的十条规范
1.API与用户的通信协议,总是使用HTTPS协议
在我们平时使用的时候,使用的HTTP协议比较多,但是这个协议有个特点是不安全的,所有的信息都是可以被人看到的,那么就会导致,如果我们再使用的过程中,不小心被人将这个信息拿去,就会导致信息的泄露,为了解决这个问题,所以这个协议就建议大家使用HTTPS协议。
2.域名
域名的书写形式有两种,第一种是写在最前面,第二种是写在最后面,格式如下
- https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
- https://example.org/api/ API很简单
3.版本
所谓的版本指的是会有好多个版本之间的更新存在,为了区分这两个的,所以会有版本的更新存在,具体书写如下
https://api.example.com/v1/ 这里面的v1就是版本的书写
4.路径
restful会将网络上的所有数据都当作资源,这些资源均使用名词表示,名词可以为复数。
https://api.example.com/v1/books/ https://api.example.com/v1/book/1 https://example.com/v2/books/api/
5.method
我们在使用上面第四个方法的时候,仅仅只能拿到图书的信息,但是具体的增删改查是怎么做的,我们并不清楚,method方法,就规定了什么方法/方式代表什么操作,具体如下。
- GET :从服务器取出资源(一项或多项)
- POST :在服务器新建一个资源
- PUT :在服务器更新资源(客户端提供改变后的完整资源)
- PATCH :在服务器更新资源(客户端提供改变的属性)
- DELETE :从服务器删除资源
6.过滤
过滤指的是在url上传参的形式传递搜索条件,比如你想查看图书,但是你不想要全部,只想要前50本,就可以使用这种方式。
https://api.example.com/v1/books?limit=50
其他示例
- https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
- https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
- https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
- https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
- https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
7.状态码
这里的状态码和我们前面学习的状态码,稍微有那么一点不同,HTTP中的状态码是规定好的,什么开头就代表什么,这里的状态码是我们自己公司规定的,每个公司都不一样,但是我们尽量还是和HTTP保持一致。
状态码详情http://tool.oschina.net/commons?type=5
几个常见的状态码
OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) NO CONTENT - [DELETE]:用户删除数据成功。 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
8.错误处理
当我们进行接口调用数据的时候,如果发生了错误,应该将错误信息返回给前端,给错误提示,是一个字典的形式
{ error: "Invalid API key" }
9.返回结果
针对客户的请求不同,要给人家不同的返回结果,返回结果规范如下
GET /collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档
其中GET返回资源对象的列表是如下形式
{'status_code': 100, 'msg': '请求成功', 'data': [{}, {}, {}, {}]}
返回单个资源对象
import json res = {'status_code': 100, 'msg': '请求成功', 'data': {'name': '红楼梦', 'price': 100}} aa = json.dumps(res, ensure_ascii=False) print(aa) #{"status_code": 100, "msg": "请求成功", "data": {"name": "红楼梦", "price": 100}}
对于剩下的几种方法,可以遵守也可以根据自己的项目需求实际修改
10.返回结果中给超链接
在实际应用中,用户可能并不一定能记住所有的接口,那么我们就要做到在用户经常用到的那个接口那里给用户返回一个个的超链接,这样用户就可以通过一个接口拿到所有的信息。具体实例
{"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }}
上面十种方法,重点需要记住的是一定要在路径中使用名词,实际上还是减少信息暴漏,以及区分不同的请求方法。
光说不练假把式,那么我们开始速速的写一个东西来看看效果吧,基于django实现
路由层
"""newBMS URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^book/', views.books) ]
视图层
from django.shortcuts import render from app01 import models from django.http import JsonResponse import json # Create your views here. def books(request): # print(request.method) back_dic = {'status_code': 100, 'msg': ''} if request.method == 'GET': book_list = models.Book.objects.all() # 这里因为前后端分离,我们都是用json格式进行数据的传输,但是json是不能序列化queryset对象的,所以我们用到了列表生成式 back_dic['data'] = [{'name': book.name, 'price': book.price} for book in book_list] # 还有就是给字典赋值 back_dic['msg'] = '查询成功' return JsonResponse(back_dic) elif request.method == 'POST': # json格式的字符串是在body中,要先解码 res = request.body.decode('utf-8') # json格式反序列化 res1 = json.loads(res) models.Book.objects.create(**res1) # # 我们说了新增数据成功以后可以不返回新增的数据对象,可以直接返回一个信息就好 back_dic['msg'] = '新增完成' return JsonResponse(back_dic)
补充:
我们在写完这些东西之后可以直接用来测试我们的接口,就避免了我们页面搭建的麻烦,这里我们使用的是postman,就是它👇
使用方法如下
然后我们拿到的json格式数据如果我们嫌他不好看,可以直接百度https://www.json.cn/,这个就可以美化你的json格式的代码!
好了,补充结束了