# 第七模块 DRF (概述)

关于前后端分离

接下来,你将进入 前后端分离项目开发 模块。 这也是现在企业中比较常见的开发模式。

疑问:

  • 什么是前后端分离?与之前的开发模式有什么区别?
  • 企业为什么要用前后端分离?

# 1. 什么是前后端分离?

  • 前后端不分离,像咱们之前学习django、案例、crm项目、bug管理 时的那些模块。

    特点:
    	- 用户访问URL
    	- 执行视图函数,视图进行业务处理
    	- 视图render,读取HTML模块+数据渲染,将渲染完成的HTML/CSS/JS返回并呈现在用户浏览器上。
    	
    配合开发:
    	- 前端,写HTML、CSS、JS
    	- 后端,前端代码给我后端,后端代码 + 前端代码 集成到项目中。
    

    image-20210828135148077

    image-20210819121547705

  • 前后端分离

    特点:
    	- 一般基于 vue.js、react.js、angular.js 框架来编写前端页面(本质上是HTML、CSS、JS)。
    	- 页面上如果需要呈现数据,则需要则需要通过 ajax 的形式向后端发送请求(URL)并获取数据。
    	- 后端接收到请求后,执行视图函数并进行业务处理
    	- 后端的视图执行完毕后,给前端返回JSON格式数据。
    	- 前端接收到JSON格式数据后呈现在浏览器上即可。
    	
    配合开发:
    	- 前端,写HTML、CSS、JS(数据都是通过调用后端API获得)
    	- 后端,写API接口
    	- 前后端约定好接口的规则。
    
    image-20210807125206445

image-20210807124425671

# 2.为什么要使用前后端分离?

目前企业一般都会采用前后端分离的形式来进行项目开发,这种模式:

  • 前后端职责清晰,前端开发者只vue.js、react.js、angular.js等框架编写页面;后端开发者只用Python编写后端代码;(两者通过json格式请求数据的传输)。
  • 开发高效,前后端做自己擅长的领域且使用vue.js等前端框架比用传统的HTML、CSS、JS、jQuery等开发速度快很多。
  • 有利于项目的扩展(开发APP、微信小程序等)。

image-20210819122740927

**注意:**前后端不分离的项目,现在一般用于开发用户量少、简单的项目。

# 关于项目安排

此项目最终是基于前后端分离来进行开发,所以对于你们来讲需要学会前端开发必备技能、后端开发必备技能后,再进行业务功能开发,所以我们的项目讲解的安排如下:

  • 第一部分:作为后端开发者,学会基于Django编写后端API(给前端提供URL并返回相应格式数据)。
  • 第二部分:作为前端开发者,学会基于vue.js编写前端页面并调用后端API获取数据。
  • 第三部分:结合前端和后端技术,开发本项目的业务功能。

后端开发

image-20210829134050933

# 3.RESTful规范

image-20210819123735927

对于后端开发者,本质上就是提供URL给前端开发者调用并返回相应的数据。例如:

image-20210807125206445

现在咱们大家知道前端后端分离的项目是需要:前端、后端 双方来进行合作开发,既然合作进行开发就必须要提前约定一些规范,以防止双方”打架“,例如:

  • 数据传输用XML格式?JSON格式?

  • 出现错误时,错误信息有谁来提供?

    方案1:错误时后端返回错误信息,前端只做呈现即可。
    	{
    		code:40001,
    		error:"xxx错误"
    	}
    方案2:错误时后端返回错误码,前端根据错误码的对应关系呈现。
    	{
    		code:40001
    	}
    
  • 等等等...

所以,我们需要先来学习下规范,然后再来进行后续开发。

restful是主流的一套API规范,企业进行前后端分离开发一般都会遵循它,他定义了很多规范的条款。

例如:如果现在让大家来开发一个对 用户表 进行增删改查的接口,在不了解restful规范前, 大家一定会这样来搞:

接口:/users/list/			用户列表
接口:/users/add/			添加用户
接口:/users/detail/(\d+)	用户详细信息
接口:/users/edit/(\d+)	更新用户
接口:/users/del/(\d+)		删除用户

很早之前开发者们也确实都是这么干的,直到后来有人提出了 restful API规范(包含了很多规定),如果按照这个规范来开发上述的功能的话:

接口:/users/			方法:GET     =>   用户列表
接口:/users/			方法:POST    =>   添加列表
接口:/users/(\d+)/	方法:GET     =>   获取单条数据
接口:/users/(\d+)/	方法:DELETE  =>   删除数据
接口:/users/(\d+)/	方法:PUT     =>   更新数据

暂且不说restful规范有多好,对于前端和后端只要能统一了规范,对大家的开发效率都会有很大的帮助,不用再做很多无效的沟通了。

所以,接下来咱们就是学习最常见的restful规范。

# 1.HTTPS协议

建议使用https协议替代http协议,让接口数据更加安全。

这一条其实与开发无关,在最后的项目部署时只要使用https部署即可。

image-20210807135928170

如果是基于HTTP协议,则意味着用户浏览器再向服务器发送数据时,都是以明文的形式传输,如果你在某咖啡厅上网,他就可以把你网络传输的数据明文都获取到。

如果是基于HTTPS协议,则意味着用户浏览器再向服务器发送数据时,都是以密文的形式传输,中途即使有非法用户获取到网络数据,也可以密文的,无法破译。

HTTPS保证了数据安全,但由数据传输存在着加密和解密的过程,所以会比HTTP协议慢一些。

注意:此处大家先了解https和http的不同,至于底层原理和部署,在项目部署时再细讲。

参考文档:https://www.cnblogs.com/wupeiqi/p/11647089.html

# 2. 域名

对于后端API接口中要体现API标识,例如:

  • https://api.example.com
  • https://www.example.com/api/

image-20210807141747844

image-20210807141524804

image-20210807142204010

# 3. 版本

对于后端API接口中要体现版本,例如:

- http://api.example.com/v1/

- http://api.example.com/?version=v1

- http://v1.example.com/

- http://api.example.com/
  请求头:Accept: application/json; version=v1

image-20210807142526704

# 4. 路径

restful API这种风格中认为网络上的一切都称是资源,围绕着资源可以进行 增删改查等操作。

这些资源,在URL中要使用名词表示(可复数),围绕着资源进行的操作就用Method不同进行区分。

https://api.example.com/v1/person
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees

# 5. 请求方法

根据请求方法不同进行不同的操作。

GET		在服务器取出资源(一项或多项)
POST	在服务器新建一个资源
PUT		在服务器更新资源(客户端提供改变后的完整资源)
PATCH	在服务器更新资源(客户端提供改变的属性)
DELETE	在服务器删除资源

例如:

https://api.example.com/v1/users
https://api.example.com/v1/users/1/

接口:/users/			方法:GET     =>   用户列表
接口:/users/			方法:POST    =>   添加用户
接口:/users/(\d+)/	方法:GET     =>   获取单条数据
接口:/users/(\d+)/	方法:DELETE  =>   删除数据
接口:/users/(\d+)/	方法:PUT     =>   更新数据
接口:/users/(\d+)/	方法:PATCH   =>   局部更新

# 6. 搜索条件

在URL中通过参数的形式来传递搜索条件。

https://api.example.com/v1/users
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. 返回数据

针对不同操作,服务器向用户返回的结果结构应该不同。

https://api.example.com/v1/users
https://api.example.com/v1/users/2/
URL 方法 描述 返回数据
/users/ GET 列表 返回资源对象的列表
[ {id:1,name:"武沛齐"}, {id:1,name:"日天"} ]
/users/ POST 添加 返回新生成的资源对象
{id:1,name:"武沛齐"}
/users/(\d+)/ GET 获取单条数据 返回单个资源对象
{id:1,name:"武沛齐"}
/users/(\d+)/ DELETE 删除数据 返回一个空文档
null
/users/(\d+)/ PUT 更新数据 返回完整的资源对象
{id:1,name:"武沛齐"}
/users/(\d+)/ PATCH 局部更新 返回完整的资源对象
{id:1,name:"武沛齐"}

一般在实际的开发过程中会对上述返回数据进行补充和完善,例如:每次请求都返回一个字典,其中包含:

  • code,表示返回码,用于表示请求执行请求,例如:0表示请求成功,1003表示参数非法,40009数据量太大等。
  • data,表示数据
  • error,错误信息
{
    code:0,
    data:[ {id:1,name:"武沛齐"},   {id:1,name:"日天"}  ]
}
{
    code:41007,
    error:"xxxxx"
}

# 8. 状态码

后端API在对请求进行响应时,除了返回数据意外还可以返回状态码,来表示请求状况。

from django.urls import path, include
from app01 import views

urlpatterns = [
    path('users/', views.users),
]
from django.http import JsonResponse

def users(request):
    info = {
        "code": 1000,
        "data": {"id":1,"name":"武沛齐"}
    }
    return JsonResponse(info, status=200)

image-20210829161449149

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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

状态码可以表示一部分的服务端的处理请求,但特别细致的信息无法全都都包括,所以一般在开发中会配合code返回码来进行。

例如:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Global_Return_Code.html

# 9. 错误处理

错误处理,状态码是4xx时,应返回错误信息,error当做key。

{
    error: "Invalid API key"
}

在 1.1.7 中中已包含。

了解常用resful规范,那么以后在开发后端API接口时,就要根据上述要求遵循restful规范(非必须,视公司情况灵活变通)。

# 4.案例展示

基于django + restful规范来开发一个后台接口示例。

# urls.py

from django.urls import path
from app01 import views

# http://www.xxx.com/api/v1/users/

urlpatterns = [
    path('api/<str:version>/users/', views.users),
    path('api/<str:version>/users/<int:pk>/', views.users),
]
# views.py

from django.http import JsonResponse


def users(request, version, pk=None):
    print("版本:", version)
    if not pk:
        if request.method == "GET":
            # 请求用户列表
            info = {
                "code": 0,
                "data": [{"id": 1, "name": "武沛齐"}]
            }
            return JsonResponse(info)
        elif request.method == "POST":
            # 新增用户,读取 request.POST 中提交的数据并添加到数据库中
            info = {
                "code": 0,
                "data": {"id": 1, "name": "武沛齐"}
            }
            return JsonResponse(info)
        else:
            info = {
                "code": 1000,
                "error": "请求错误"
            }
            return JsonResponse(info)

    if request.method == "GET":
        # 获取ID=pk的用户信息,并返回
        info = {
            "code": 0,
            "data": {"id": 1, "name": "武沛齐"}
        }
        return JsonResponse(info)
    elif request.method == "DELETE":
        # 删除id=pk的用户
        info = {
            "code": 0,
            "data": {}
        }
        return JsonResponse(info)
    elif request.method == "PUT":
        # 读取request.POST中的数据 + pk,更新数据库中的用户信息
        info = {
            "code": 0,
            "data": {"id": 1, "name": "武沛齐"}
        }
        return JsonResponse(info)
    elif request.method == "PATCH":
        # 读取request.POST中的数据 + pk,更新数据库中的用户信息
        info = {
            "code": 0,
            "data": {"id": 1, "name": "武沛齐"}
        }
        return JsonResponse(info)
    else:
        info = {
            "code": 1000,
            "error": "请求错误"
        }
        return JsonResponse(info)

# 5.Django的FBV和CBV

基于django开发项目时,对于视图可以使用 FBV 和 CBV 两种模式编写。

  • FBV,function base views,其实就是编写函数来处理业务请求。

    from django.contrib import admin
    from django.urls import path
    from app01 import views
    urlpatterns = [
        path('users/', views.users),
    ]
    
    from django.http import JsonResponse
    
    def users(request,*args, **kwargs):
        if request.method == "GET":
            return JsonResponse({"code":1000,"data":"xxx"})
        elif request.method == 'POST':
            return JsonResponse({"code":1000,"data":"xxx"})
        ...
    
  • CBV,class base views,其实就是编写类来处理业务请求。

    from django.contrib import admin
    from django.urls import path
    from app01 import views
    urlpatterns = [
        path('users/', views.UserView.as_view()),
    ]
    
    from django.views import View
    
    class UserView(View):
        def get(self, request, *args, **kwargs):
            return JsonResponse({"code": 1000, "data": "xxx"})
    
        def post(self, request, *args, **kwargs):
            return JsonResponse({"code": 1000, "data": "xxx"})
    

其实,CBV和FBV的底层实现本质上相同的。

image-20210819114755157

image-20210819115517407

CBV,其实就是在FBV的基础上进行的功能的扩展,根据请求的方式不同,直接定位到不同的函数中去执行。

如果是基于django编写restful API,很显然使用CBV的方式会更加简洁,因为restful规范中就是根据method不同来执行不同操作。

基于django的CBV和restful规范开发实战案例:

# urls.py

from django.urls import path
from app01 import views

urlpatterns = [
    # http://www.xxx.com/api/v1/users/
    path('api/<str:version>/users/', views.UserView.as_view()),

    # http://www.xxx.com/api/v1/users/2/
    path('api/<str:version>/users/<int:pk>/', views.UserView.as_view()),

]
# views.py

from django.views import View
from django.http import JsonResponse


class UserView(View):
    def get(self, request, version, pk=None):
        if not pk:
            # 请求用户列表
            info = {
                "code": 0,
                "data": [
                    {"id": 1, "name": "武沛齐"},
                    {"id": 1, "name": "武沛齐"},
                ]
            }
            return JsonResponse(info)
        else:
            # 获取ID=pk的用户信息,并返回
            info = {
                "code": 0,
                "data": {"id": 1, "name": "武沛齐"}
            }
            return JsonResponse(info)

    def post(self, request, version):
        # 新增用户,读取 request.POST 中提交的数据并添加到数据库中
        info = {
            "code": 0,
            "data": {"id": 1, "name": "武沛齐"}
        }
        return JsonResponse(info)

    def delete(self, request, version, pk):
        # 删除id=pk的用户
        info = {
            "code": 0,
            "data": {}
        }
        return JsonResponse(info)

    def put(self, request, version, pk):
        # 读取request.POST中的数据 + pk,更新数据库中的用户信息
        info = {
            "code": 0,
            "data": {"id": 1, "name": "武沛齐"}
        }
        return JsonResponse(info)

    def patch(self, request, version, pk):
        # 读取request.POST中的数据 + pk,更新数据库中的用户信息
        info = {
            "code": 0,
            "data": {"id": 1, "name": "武沛齐"}
        }
        return JsonResponse(info)

从上面的示例看来,基于django框架完全可以开发restful API。

django restframework框架 是在django的基础上又给我们提供了很多方便的功能,让我们可以更便捷基于django开发restful API,来一个简单的实例,快速了解下:

  • 基于django image-20210819132015065
  • 基于django + django restframework框架 image-20210819132209726
上次更新: 8/25/2022, 12:14:54 PM