「DRF系列」-DRF初识
Web应用模式
前后端不分离
之前我们学习过Django是基于MTV模式的,页面是由后端进行渲染,然后浏览器进行解析显示内容。
在前后端不分离的应用模式中,前端页面看到的效果都是由后端控制,由后端渲染页面或重定向,也就是后端需要控制前端的展示,前端与后端的耦合度很高。
这种应用模式比较适合纯网页应用。
但是在移动互联网时代,当后端对接各种App,小程序,H5,PC网站,这种情况下并不需要后端返回一个HTML页面,而仅仅是数据本身,所以后端返回HTML网页的前后端模式不再适用于APP,小程序之类,为了对接,还需要再开发一套接口。
但是这样非常麻烦,无论何种前端,所需的数据基本相同,后端仅需开发一套逻辑对外提供数据即可,这就需要使用前后端分离模式。
前后端分离
在前后端分离模式中,后端仅返回前端所需的数据,不再渲染HTML页面,不再控制前端的效果,至于前端用户看到什么效果,从后端请求的数据如何加载到前端中,都由前端自行决定,网页有网页的处理方式,App有App的处理方式,但无论哪种前端,所需的数据基本相同,后端仅需开发一套逻辑对外提供数据即可。
在前后端分离的应用模式中 ,前端与后端的耦合度相对较低。
在前后端分离的应用模式中,我们通常将后端开发的每个视图都称为一个接口,或者API,前端通过访问接口来对数据进行增删改查。
前后端分离的优点
1) 彻底解放前端 前端不再需要向后台提供模板或是后台在前端html中嵌入后台代码。
2)提高工作效率,分工更加明确 前后端分离的工作流程可以使前端只关注前端的事,后台只关心后台的活,两者开发可以同时进行,在后台还没有时间提供接口的时候,前端可以先将数据写死或者调用本地的json文件即可,页面的增加和路由的修改也不必再去麻烦后台,开发更加灵活。
3)局部性能提升 通过前端路由的配置,我们可以实现页面的按需加载,无需一开始加载首页便加载网站的所有的资源,服务器也不再需要解析前端页面,在页面交互及用户体验上有所提升。
4)降低维护成本 通过MVC框架,我们可以非常快速的定位及发现问题的所在,客户端的问题不再需要后台人员参与及调试,代码重构及可维护性增强。
RESTful
RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增、变更、删除所调用资源。
例如,对于后端数据库中保存了文章的信息,前端可能需要对文章数据进行增删改查,那相应的每个操作后端都需要提供一个API接口:
POST /add-article # 添加文章DELETE /del-article # 删除文章PUT /edit-article # 修改文章GET /get-article # 查询文章
对于接口的请求方式与路径,每个后端开发人员可能都有自己的定义方式,风格迥异。是否存在一种统一的定义方式,被广大开发人员接受认可的方式呢?这就是被普遍采用的API的RESTful设计风格。
RESTful架构设计规范
域名
推荐应该尽量将api部署在专用域名下,如:https://api.qmpython.com。
如果API很简单,不会进一步扩展,则直接放在主域名下也可以,如:https://www.qmpython.com/api/
版本
除了域名设计,建议将API的版本号放在HTTP头信息中,这样简化了url复杂度。
因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URL,版本号可以在HTTP请求头信息的Accept字段中进行区分。
路径
路径,标识api的具体网址,每个网址代表一种资源。
1)资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应。
# 野生写法/getArticles/listArticles/editArticle?id=1
对于一个简洁结构,应该尽量使用名词。此外,利用的HTTP方法可以分离网址中的资源名称的操作。
GET: /articles # 查询所有文章POST: /articles # 添加文章GET: /articles/2 # 查询id=2的文章PUT: /articles/2 # 更新id=2的文章DELETE: /articles/2 # 删除id=2的文章
2)API中的名词应该是复数,无论所有资源还是单个资源。
https://www.qmpython.com/api/articles/2 # 获取单个文章https://www.qmpython.com/api/articles # 获取所有文章
HTTP动词
对于资源的具体操作类型,由HTTP动词表示,常用的HTTP动词4个:
GET # 获取资源(1个或多个)POST # 新建一个资源PUT # 更新资源(客服端提供改变后的完整资源)DELETE # 删除资源
不常用的HTTP动词:
OPTIONS # 获取信息,关于资源的哪些属性是客户端可以改变的HEAD # 获取资源的元数据PETCH(update) # 在服务器更新(更新)资源(客户端提供改变的属性)
举例说明:
GET: /articles # 列出所有文章POST: /articles # 新建一个文章(上传文件)GET: /articles/ID # 获取某个指定文章的信息PUT: /articles/ID # 更新某个指定文章的信息(提供该文章的全部信息)PATCH: /articles/ID # 更新某个指定文章的信息(提供该文章的部分信息)DELETE: /articles/ID # 删除某个文章
过滤信息
如果返回记录数量很多,服务器不可能都将他们返回给用户,API应该提供参数,过滤返回的结果。如以下常见示例过滤参数:
?limit=10 # 指定返回的记录数量?offset=10 # 指定反馈记录的开始位置?page=2&per_page=100 # 指定第几页,以及每页的记录数量?sortby=name&order=asc # 指定返回的结果按那个属性进行排序,以及如何排序?animal_type_id=1 # 指定筛选条件
状态码
服务器向用户返回的状态码和提示信息,常见的有以下一些(括号中是该状态吗对应的HTTP动词)。
200 OK - [GET] # 服务器成功返回用户请求的数据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 - [*] # 服务器发生错误,用户将无法判断发出的请求是否成功。
错误处理
如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
{ error: "Invalid API key"}
返回结果
针对不同操作,服务器向用户返回的结果应该符合以下规范。
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表示返回类型。
比如,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"}
上面代码表示,服务器给出了提示信息,以及文档的网址。
其他
服务器返回的数据格式,应该尽量使用JSON,避免使用XML。
restful开发的核心
在开发REST API时,视图主要做了3件事:
将请求的数据(如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.bodyjson_str = json_bytes.decode()# 反序列化book_dict = json.loads(json_str)book = BookInfo.objects.create( btitle=book_dict.get('btitle'), bpub_date=datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date())
总结
在开发REST API时,视图中要频繁的进行序列化与反序列化的编写。在开发REST API接口时,我们在视图中需要做的最核心的事是:
- 将数据库数据序列化为前端所需要的格式,并返回;
- 将前端发送的数据反序列化为模型类对象,并保存到数据库中。
Django REST framework
在序列化与反序列化时,虽然操作的数据不尽相同,但是执行的过程却是相似的,也就是说这部分代码是可以复用简化编写的。
在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:
- 增:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回
- 删:判断要删除的数据是否存在 -> 执行数据库删除
- 改:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回
- 查:查询数据库 -> 将数据序列化并返回
Django REST framework可以帮助我们简化上述两部分的代码编写,大大提高REST API的开发速度。
DRF简介
Django REST framework框架是一个用于构建Web API的强大而又灵活的工具。通常简称为DRF框架 。
DRF框架是建立在Django框架基础之上,二次开发的开源项目。它有以下特点:
- 提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
- 提供了丰富的类视图、Mixin扩展类,简化视图的编写;
- 丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
- 多种身份认证和权限认证方式的支持;
- 内置了限流系统;
- 直观的 API web 界面;
- 可扩展性,插件丰富;
DRF安装配置
Django REST framework(DRF),是以Django扩展应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建。(若没有Django环境,需要先创建环境安装Django)
1)下载安装,在创建的虚拟环境中安装Django REST framework
pip install djangorestframework
2)在settings.py中添加rest_framework应用
INSTALLED_APPS = [ ... 'rest_framework',]
接下来就可以使用DRF进行开发了。