请求响应组件

Django的CBV

FBV(function base views) 就是在视图里使用函数处理请求.

CBV(class base views) 就是在视图里使用类处理请求.

Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态).所以

Django在后来加入了Class-Based—View.可以让我们用类写View.

这样做的优点主要下面两种:

  1. 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
  2. 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性

CBV简单示例

我们简单来看下如何使用CBV模式,然后再分析下源代码是如何执行的,源码之后在讲解

urls.py中进行路由配置

1
2
3
urlpatterns = [
url(r'^login/$',views.LoginView.as_view()), # FBV,CBV不管怎么样调用,返回的一定是个函数.
]

views视图中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.shortcuts import render,HttpResponse,redirect
from django.views import View

class LoginView(View):
def get(self,request):
print("GET")
return render(request,"login.html")

def post(self,request):
print("POST")
user=request.POST.get("user")
pwd=request.POST.get("pwd")

return HttpResponse("OK")

进行测试http://localhost:8000/login/

源码分析

我们分析源码从路由配置开始,

1
2
3
4
5
6
7
8
9
10
11
from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', views.LoginView.as_view()), # 最后必须返回一个函数变量
# views 我们自己定义的一个模块
# LoginView views 我们定义的类
# as_view LoginView下的一个方法(函数)
]

这里的url配置我们可以看出,浏览器访问index会执行它对应的函数views.LoginView.as_view(),那么views.LoginView.as_view()又干了什么呢?

  1. 首先我们去views.LoginView找要执行的as_view(),你会发现找不到,我们接着去他的父类View里边找找到了,我们把代码贴出来分析一下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@classonlymethod  # @classonlymethod的意思是得as_view只允许使用用类对象来调用
def as_view(cls, **initkwargs):
"""
请求-响应过程的主要入口点。
"""
for key in initkwargs: # 如果你执行as_view时带入参数了这里就会循环判断
if key in cls.http_method_names: # 下边有解析
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))

def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs

# take name and docstring from class
update_wrapper(view, cls, updated=())

# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view # 最后返回的是一个函数变量,函数变量指向一个存储该函数的内存地址

由于我们的配置中views.IndexView.as_view()参数为null,所以在for key in initkwargs会直接跳过,如果不为null,就去判断执行接下来的代码,大概意思就是,if key in cls.http_method_names:if not hasattr(cls, key):,如果传递过来的字典中某键包含在  http_method_names 列表中和本类中没有该键属性都会跑出异常,http_method_names列表如下

根据上边刨析,我们可以得出

1
2
3
4
5
6
url(r'^login/', views.LoginView.as_view()), 
# 仔细看V的大小写,此Views非上边的views了咯
# 父类中找到了as_view
url(r'^login/', Views.as_view()),
# 父类中的as_view最后返回的是view这个函数变量
url(r'^login/', Views.view()),
  1. 用户一旦访问login—那么Views.view(request)就会被调用执行.只要视图函数被调用,url会自动传入一个request

    那我们现在来view函数干了什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls是当前这个类,cls加括号实例化一个对象-self
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 赋给self实例变量
return self.dispatch(request, *args, **kwargs)
# 最后view返回了self.dispatch(request, *args, **kwargs),dispatch也是最重要的,所以这里的执行结果返回什么,用户看到的就是什么
view.view_class = cls
view.view_initkwargs = initkwargs

# take name and docstring from class
update_wrapper(view, cls, updated=())

# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view # 最后返回的是一个函数变量,函数变量指向一个存储该函数的内存地址

经过上边我们可以得出的结论

这里的执行结果返回什么,用户看到的就是什么

1
2
view(request):	
return self.dispatch(request, *args, **kwargs)
  1. 现在我们就要确定的是self是谁?

img

经上图得出的结论:

self.dispatch 会先从自己(LoginView)类里边找dispatch

self.dispatch加()了,所以这是一个实例方法的调用执行

所以 dispatch 返回什么view()就返回什么

1
2
view(request):	
return self.dispatch(request, *args, **kwargs)
  1. LoginView类里自己有dispatch的话就调用自己的,没有的话就去自己的父类里边找;

    我们在LoginView类里边没找到dispatch,所以还得去他的父类里边找dispatch

以下是View类里的dispatch函数

1
2
3
4
5
6
7
8
9
10
11
12
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
# 如果你请求的方法在self.http_method_names这里面就getattr(反射)去你的类里边找你请求的方法(request.method.lower()),找不到就以这个(self.http_method_not_allowed)返回405
# http_method_names自己类里没有记住要去他的父类找哦!
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
# 如果你请求的方法不在self.http_method_names这里面就报错
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)

dispatch就是分发不同的请求方法走对应的函数,所以自己的视图函数写不同方法执行后返回的结果

view视图中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.shortcuts import render,HttpResponse,redirect
from django.views import View

class LoginView(View):
# 如果是GET请求request.method.lower()就相当于get()执行后返回结果,赋值给handler并且返回
def get(self,request):
print("GET")
return render(request,"login.html")
# 如果是POST请求request.method.lower()就相当于post()执行后返回结果,赋值给handler并且返回
def post(self,request):
print("POST")
user=request.POST.get("user")
pwd=request.POST.get("pwd")

return HttpResponse("OK")

总结:

as_view()返回的结果就是—> view()返回的结果就是—> dispatch()返回的结果就是—>handler()返回的结果—> 自己定义请求方法函数的返回结果,否则就抛错405

self是调用as_view()类的实例化

类的查找:不管什么时候调用什么先去自己类里面找,没有就去自己的父类里面找

405 - 用来访问本页面的 HTTP 谓词不被允许(方法不被允许)

Django_Rest_Framework

核心思想: 大量缩减编写api接口的代码

Django REST framework是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持。REST framework提供了一个API 的Web可视化界面来方便查看测试接口。

中文文档:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework

github: https://github.com/encode/django-rest-framework/tree/master

特点:

  • 提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
  • 提供了丰富的类视图、Mixin扩展类,简化视图的编写;
  • 丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
  • 多种身份认证和权限认证方式的支持;[jwt]
  • 内置了限流系统;
  • 直观的 API web 界面;【方便我们调试开发api接口】
  • 可扩展性,插件丰富

安装

DRF需要以下依赖:

  • Python (3.5 以上)
  • Django (2.2 以上)

DRF是以Django子应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建。(若没有Django环境,需要先创建环境安装Django)

前提是已经安装了django,建议安装在虚拟环境

1
2
3
4
##清华源
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple djangorestframework
## 默认
pip3 install djangorestframework

配置

settings.py注册app

1
2
3
4
5
6
## Application definition

INSTALLED_APPS = [
........
'rest_framework',
]

APIView使用

1
rest_framework.views.APIView

APIView是REST framework提供的所有视图的基类,继承自Django的View父类。

相较于CBV,路由不变,视图不变,继承类改为APIView

新建一个app

1
python manage.py startapp app

app/views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 视图层
from rest_framework.views import APIView
from rest_framework.response import Response
user_list = [{'id': 1, 'name': 'Bob'}, {'id': 2, 'name': 'Tom'}]
class Users(APIView):
def get(self, request, *args, **kwargs):
return Response({
'status': 0,
'msg': 'ok',
'results': user_list
})
def post(self, request, *args, **kwargs):
# request对formdata,urlencoded,json三个格式参数均能解析
name = request.data.get('name')
id = len(user_list) + 1
user = {'id': id, 'name': name}
user_list.append(user)
return Response({
'status': '0',
'msg': 'ok',
'results': user
})

主路由

1
2
3
4
5
# 路由层
from app import views
urlpatterns = [
url(r'^users/', views.Users.as_view()),
]

访问[Users – Django REST framework](http://127.0.0.1:8000/users/)返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"status": 0,
"msg": "ok",
"results": [
{
"id": 1,
"name": "Bob"
},
{
"id": 2,
"name": "Tom"
}
]
}

APIView源码分析

前言:APIView基于View 看这部分内容一定要懂CBV源码分析里的内容

在CBV源码分析中,我们是分析的from django.views import View下的执行流程

这篇博客我们就来了解下APIView是如何执行的,跟django.views模块下的view有何关联?

我们依然从urls.py配置入手分析

1
2
3
4
5
6
7
from django.conf.urls import url
from django.contrib import admin
from app import views

urlpatterns = [
url(r'^users/', views.Users.as_view()),
]

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 视图层
from rest_framework.views import APIView
from rest_framework.response import Response
user_list = [{'id': 1, 'name': 'Bob'}, {'id': 2, 'name': 'Tom'}]
class Users(APIView):
def get(self, request, *args, **kwargs):
return Response({
'status': 0,
'msg': 'ok',
'results': user_list
})
def post(self, request, *args, **kwargs):
# request对formdata,urlencoded,json三个格式参数均能解析
name = request.data.get('name')
id = len(user_list) + 1
user = {'id': id, 'name': name}
user_list.append(user)
return Response({
'status': '0',
'msg': 'ok',
'results': user
})

1、首先我们还是来确认urls.pyas_view是谁执行的?

首先我们去views.PublishView中找,发现找不到,所以我们接着再去PublishView的父类APIView中去找,找到了所以

执行调用APIView.as_view(),内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class APIView(View):

# 多余的代码暂且删掉了
@classmethod
def as_view(cls, **initkwargs):
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation

# 1. super调用父类的as_view的执行结果赋值给view这个变量
view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs

# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.

# csrf_exempt 不在执行调用csrf_token那个中间件
# 中间件是针对全局的
# 2. 这里返回的view就是父类View.view
return csrf_exempt(view)

def dispatch(self, request):
# (1)构建新的request对象
request = self.initialize_request(request, *args, **kwargs)
self.request = request
# (2)认证、权限、限流组件三件套
self.initial(request, *args, **kwargs)
# (3)分发
handler = getattr(self, request.method.lower(),self.http_method_not_allowed) # 按请求方式分发
return handler(request, *args, **kwargs)

2、大家是不是以为这样就结束了?NO!NO!NO!

如果是as_view是View的as_view,dispatch是View的dispatch,那rest-framework不就成废钞了么?

as_view的执行结果是dispatch的执行结果,那么dispatch还是View的dispatch么?

  1. 我们先看下views.PublishView里边有没有dispatch,发现没有
  2. 我们在views.PublishView的父类APIView,发现有
  3. 所以dispatch是APIView.dispatch非View.dispatch
  4. APIView.dispatch 里边有一堆组件,这里不说

中间的这点代码跟之前的View.dispatch一样:

还是做请求分发,在请求之外又做了一堆事情

1
2
3
4
5
6
7
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed

response = handler(request, *args, **kwargs)

总结:

url 转变过程

  1. url(r’^publishes/', views.PublishView.as_view()),
  2. url(r’^publishes/', APIView.as_view()),
  3. url(r’^publishes/', View.as_view()),

View.as_view()我们在django—CBV讲解过了

所以最后调用还是,只是APIView.as_view在里边加了一些他自己定义的一些东西,只是这里没讲到而已

APIView.as_view()—> View.as_view()—> APIView.dispatch()—>response—>handler()–> 自己定义请求方法函数的返回结果,否则就抛错405

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
## as_view()
# 核心走了父类as_view
view = super(APIView, cls).as_view(**initkwargs)
# 返回的是局部禁用csrf认证的view视图函数
return csrf_exempt(view)

## dispatch(self, request, *args, **kwargs)
# 二次封装request对象
request = self.initialize_request(request, *args, **kwargs)
# 自定义request规则
self.initial(request, *args, **kwargs)

## initialize_request(self, request, *args, **kwargs)
# 原生request封装在request._request

## initial(self, request, *args, **kwargs)
# 认证
self.perform_authentication(request)
# 权限
self.check_permissions(request)
# 频率
self.check_throttles(request)

APIViewView的不同之处在于:

  • 传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
  • 视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端期望要求的格式;
  • 任何APIException异常都会被捕获到,并且处理成合适格式的响应信息返回给客户端;
  • 重新声明了一个新的as_views方法并在dispatch()进行路由分发前,会对请求的客户端进行身份认证、权限检查、流量控制。

DRF在django原有的基础上,新增了一个request对象继承到了APIVIew视图类,并在django原有的HttpResponse响应类的基础上实现了一个子类rest_framework.response.Response响应类。这两个类,都是基于内容协商来完成数据的格式转换的。

  • request->parser->识别客户端请求头中的Content-Type来完成数据转换成->类字典(QueryDict,字典的子类)

  • response->renderer->识别客户端请求头的"Accept"来提取客户单期望的返回数据格式,-> 转换成客户端的期望格式数据

请求模块

REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。

REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request对象中。

Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。

无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。

常用属性

request.data 返回解析之后的请求体数据。类似于Django中标准的request.POSTrequest.FILES属性,但提供如下特性:

  • 包含了解析之后的文件和非文件数据
  • 包含了对POST、PUT、PATCH请求方式解析后的数据
  • 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据

request.query_params与Django标准的request.GET相同,只是更换了更正确的名称而已。

request._request 获取django封装的Request对象

基本使用

视图代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from django.views import View
from django.http.response import HttpResponse
from django.http.request import HttpRequest
from django.core.handlers.wsgi import WSGIRequest
class ReqView(View):
def get(self,request):
print(request)
return HttpResponse("ok")

"""
默认情况下, 编写视图类时,如果继承的是django内置的django.view.View视图基类,
则视图方法中得到的request对象,是django默认提供的django.core.handlers.wsgi.WSGIRequest
WSGIRequest这个请求处理对象,无法直接提供的关于json数据数据处理。
在编写api接口时很不方便,所以drf为了简写这块内容,在原来的HttpRequest的基础上面,新增了一个Request对象
这个Request对象是单独声明的和原来django的HttpRequest不是父子关系。
同时注意:
要使用drf提供的Request请求处理对象,必须在编写视图类时继承drf提供的视图基类
from rest_framework.views import APIView

如果使用drf提供的视图基类APIView编写类视图,则必须使用来自drf提供的Request请求对象和Response响应对象
"""
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class ReqAPIView(APIView):
def get(self,request):
# rest_framework.request.Request对象
print(request) # <rest_framework.request.Request: GET '/req/req2?name=xiaoming&age=17&lve=swim&lve=code'>
# 获取查询字符串
print(request.query_params)
# 没有参数情况下: <QueryDict: {}>
# 有参数的情况下: <QueryDict: {'name': ['xiaoming'], 'age': ['17'], 'lve': ['swim', 'code']}>
# 所以,request.query_params的返回值操作和原来在django里面是一模一样的
print(request.query_params.get("name")) # xiaoming
print(request.query_params.getlist("lve")) # ['swim', 'code']

return Response("ok")

def post(self, request):
# 获取请求体
print(request.data) # {'name': 'xiaoming', 'age': 16, 'lve': ['swim', 'code']}
"""直接从请求体中提取数据转
# 客户端如果上传了json数据,直接返回字典
{'name': '灰太狼', 'age': 20, 'sex': 1, 'classmate': '301', 'description': '我还会再回来的~'}
# 客户端如果上传了表单数据,直接返回QueryDict
<QueryDict: {'name': ['xiaohui'], 'age': ['18']}>
"""
print(request.FILES) # 获取上传文件列表

# 要获取django原生提供的HttpRequest对象,可以通过request._request来获取到
print(request._request.META.get("Accept")) # 当值为None时,drf默认在响应数据时按json格式返回
# response = Response(data="not ok", status=204, headers={"Company":"Oldboy"})
response = Response(data="not ok", status=status.HTTP_400_BAD_REQUEST, headers={"Company":"Oldboy"})
return response

响应模块

1
rest_framework.response.Response

REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染器)成符合前端需求的类型。

REST framework提供了Renderer 渲染器,用来根据请求头中的Accept(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用Content-Type方式处理响应数据,我们可以通过配置来修改默认响应格式。

可以在rest_framework.settings查找所有的drf默认配置项

1
2
3
4
5
6
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器,返回json数据
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览器API渲染器,返回调试界面
)
}

构造方式

1
Response(data, status=None, template_name=None, headers=None, content_type=None)

drf的响应处理类和请求处理类不一样,Response就是django的HttpResponse响应处理类的子类。

data数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用renderer渲染器处理data

data不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用Serializer序列化器序列化处理后(转为了Python字典类型)再传递给data参数。

参数说明:

  • data: 为响应准备的序列化处理后的数据;
  • status: 状态码,默认200;
  • template_name: 模板名称,如果使用HTMLRenderer 时需指明;
  • headers: 用于存放响应头信息的字典;
  • content_type: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数

response对象的属性

.data:传给response对象的序列化后,但尚未render处理的数据

.status_code:状态码的数字

.content:经过render处理后的响应数据

状态码

为了方便设置状态码,REST framewrok在rest_framework.status模块中提供了常用http状态码的常量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 1)信息告知 - 1xx
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS

# 2)成功 - 2xx
HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS

# 3)重定向 - 3xx
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT

# 4)客户端错误 - 4xx
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS

# 5)服务器错误 - 5xx
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED

解析模块

1、解析器的作用

根据请求头 content-type 选择对应的解析器对请求体内容进行处理。

有application/json,x-www-form-urlencoded,form-data等格式

2、全局使用解析器

  • settings.py配置
1
2
3
4
5
6
7
8
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':[
'rest_framework.parsers.JSONParser'
'rest_framework.parsers.FormParser'
'rest_framework.parsers.MultiPartParser'
]

}
  • urls.py配置
1
2
3
urlpatterns = [
url(r'test/', TestView.as_view()),
]
  • 视图函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from rest_framework.views import APIView
from rest_framework.response import Response

class TestView(APIView):
def post(self, request, *args, **kwargs):
print(request.content_type)

# 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST请求,响应内容')

def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

3、局部使用解析器

3-1 仅处理请求头content-type为application/json的请求体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from django.conf.urls import url, include
from web.views.s5_parser import TestView

urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]
##!/usr/bin/env python
## -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser

class TestView(APIView):
parser_classes = [JSONParser, ]

def post(self, request, *args, **kwargs):
print(request.content_type)

# 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)

# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)

return Response('POST请求,响应内容')

def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

3-2 仅处理请求头content-type为application/x-www-form-urlencoded 的请求体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]
##!/usr/bin/env python
## -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FormParser

class TestView(APIView):
parser_classes = [FormParser, ]

def post(self, request, *args, **kwargs):
print(request.content_type)

# 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)

# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)

return Response('POST请求,响应内容')

def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

3-3 仅处理请求头content-type为multipart/form-data的请求体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]
##!/usr/bin/env python
## -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import MultiPartParser

class TestView(APIView):
parser_classes = [MultiPartParser, ]

def post(self, request, *args, **kwargs):
print(request.content_type)

# 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST请求,响应内容')

def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')
<!DOCTYPE html>
<html lang="en">
<head>

<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data">
<input type="text" name="user" />
<input type="file" name="img">

<input type="submit" value="提交">

</form>
</body>
</html>

3-4 仅上传文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
url(r'test/(?P<filename>[^/]+)', TestView.as_view(), name='test'),
]
##!/usr/bin/env python
## -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FileUploadParser

class TestView(APIView):
parser_classes = [FileUploadParser, ]

def post(self, request, filename, *args, **kwargs):
print(filename)
print(request.content_type)

# 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST请求,响应内容')

def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')
<!DOCTYPE html>
<html lang="en">
<head>

<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data">
<input type="text" name="user" />
<input type="file" name="img">

<input type="submit" value="提交">

</form>
</body>
</html>

3-5 同时多个Parser

当同时使用多个parser时,rest framework会根据请求头content-type自动进行比对,并使用对应parser

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
url(r'test/', TestView.as_view(), name='test'),
]
##!/usr/bin/env python
## -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser

class TestView(APIView):
parser_classes = [JSONParser, FormParser, MultiPartParser, ]

def post(self, request, *args, **kwargs):
print(request.content_type)

# 获取请求的值,并使用对应的JSONParser进行处理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST请求,响应内容')

def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')

4、源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
## 1 在调用request.data时,才进行解析,由此入手
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data

## 2 查看self._load_data_and_files()方法---->self._data, self._files = self._parse()

def _parse(self):
#用户请求头里content_type的值
media_type = self.content_type

#self.parsers 就是用户配置的parser_classes = [FileUploadParser,FormParser ]
#self里就有content_type,传入此函数
parser = self.negotiator.select_parser(self, self.parsers)

## 3 查看self.negotiator.select_parser(self, self.parsers)
def select_parser(self, request, parsers):
#同过media_type和request.content_type比较,来返回解析器,然后调用解析器的解析方法
#每个解析器都有media_type = 'multipart/form-data'属性
for parser in parsers:
if media_type_matches(parser.media_type, request.content_type):
return parser
return None

## 4 最终调用parser的解析方法来解析parsed = parser.parse(stream, media_type, self.parser_context)
## 1 Request实例化,parsers=self.get_parsers()
Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
## 2 get_parsers方法,循环实例化出self.parser_classes中类对象
def get_parsers(self):
return [parser() for parser in self.parser_classes]

## 3 self.parser_classes 先从类本身找,找不到去父类找即APIVIew 中的
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
## 4 api_settings是一个对象,对象里找DEFAULT_PARSER_CLASSES属性,找不到,会到getattr方法
def __getattr__(self, attr):
if attr not in self.defaults:
raise AttributeError("Invalid API setting: '%s'" % attr)

try:
#调用self.user_settings方法,返回一个字典,字典再取attr属性
val = self.user_settings[attr]
except KeyError:
# Fall back to defaults
val = self.defaults[attr]

# Coerce import strings into classes
if attr in self.import_strings:
val = perform_import(val, attr)

# Cache the result
self._cached_attrs.add(attr)
setattr(self, attr, val)
return val
# 5 user_settings方法 ,通过反射去setting配置文件里找REST_FRAMEWORK属性,找不到,返回空字典
@property
def user_settings(self):
if not hasattr(self, '_user_settings'):
self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
return self._user_settings

jQuery

1
>: cnpm install jquery0

vue/cli 3 配置jQuery:在vue.config.js中配置(没有,手动项目根目录下新建)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const webpack = require("webpack");

module.exports = {
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
Popper: ["popper.js", "default"]
})
]
}
};

BootStrap

1
>: cnpm install bootstrap@3

vue/cli 3 配置BootStrap:在main.js中配置

1
2
import "bootstrap"
import "bootstrap/dist/css/bootstrap.css"