跳到主要内容

[toc]

django中间件

1.中间件介绍

1.1 django中间件简介及定义

django中间件是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

Django的中间件的定义:

#中间件是一个钩子框架,用于Django的请求/响应处理。这是一个轻量级的、底层的“插件”系统,用于全局地改变Django的输入或输出。
Middleware is a framework of hooks into Django’s request/response processing. <br>It’s a light, low-level “plugin” system for globally altering Django’s input or output.

1.2 django中间件的作用

如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。

可能你还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。

说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。

1.3 django中间件的配置项

打开django项目的settings.py文件,看到下面的MIDDLEWARE配置项,django默认自带的一些中间件:

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

2.django生命周期

django生命周期

iShot_2024-08-30_16.41.16

3.自定义中间件

第一步、在django项目下的应用程序目录下创建一个mymiddleware目录,名称任意,在这个目录中再创建一个middleware文件,名称任意,在这个文件中写入以下内容

#导入MiddlewareMixin
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect,render,HttpResponse


class Auth(MiddlewareMixin):
# 定义一个白名单
white_list = ['/login/', ]

def process_request(self,request):

if request.path in self.white_list:
pass
else:
is_login = request.session.get('is_login')
"""
request.session.get做的事
1.取出cookie中的session随机字符串{'sessionid':'asdfasfpoaijsdgihsdj'}
xx = request.COOKIES.get('sessionid')

2.到数据库中查询这个sessionid对应的那条记录
data = select session_data from django_session where session_key = xx;

3.拿出记录中的session_data数据部分进行解密,并取出数据
dic = sss(data) --> {'is_login':True}
dic.get('is_login') --> True
"""

# 这里写一个pass,就是返回一个None,如果返回是None,则说明通过了请求
if is_login == True:
pass
else:
# return redirect('login')
return HttpResponse('中间件不让你登陆')

第二步、在settings文件中MIDDLEWARE项的最后边配置中间件

#应用程序名.自定义中间件的目录.自定义中间件py文件.自定义类
'app01.mymiddleware.middleware.Auth'

第三步、视图文件

from django.shortcuts import render,HttpResponse,redirect
from django.urls import reverse
# Create your views here.

def login(request):
if request.method == 'GET':
return render(request,'login.html')

else:
username = request.POST.get('username')
password = request.POST.get('password')

if username == 'admin' and password == 'admin':

request.session['is_login'] = True
request.session['username'] = username

"""
设置session后做的3件事
1 生成随机字符串
2 放到cookie中进行传输
3 将随机字符串和对应数据保存到自己服务端的数据库中
"""

return redirect('home')
else:
return redirect('login')


def home(request):
#这里获取上边我们自己设置的cookie
# is_login = request.COOKIES.get('is_login')
is_login = request.session.get('is_login')
"""
request.session.get做的事
1.取出cookie中的session随机字符串{'sessionid':'asdfasfpoaijsdgihsdj'}
xx = request.COOKIES.get('sessionid')

2.到数据库中查询这个sessionid对应的那条记录
data = select session_data from django_session where session_key = xx;

3.拿出记录中的session_data数据部分进行解密,并取出数据
dic = sss(data) --> {'is_login':True}
dic.get('is_login') --> True
"""

if is_login == True:
return render(request,'home.html')
else:
return redirect('login')

4.中间件5大方法

4.1 process_request(self,request)

3中的自定义中间件用到的就是process_request(self,request),这是一个对请求进行处理的方法

process_request有一个参数,就是request,这个request和视图函数中的request是一样的。

它的返回值可以是None也可以是HttpResponse对象。返回值是None的话,按正常流程继续走,交给下一个中间件处理,如果是HttpResponse对象,Django将不执行视图函数,而将响应对象返回给浏览器。

4.2 process_response(self, request, response)

process_response是对视图函数所有的响应进行处理

它有两个参数,一个是request,一个是response,request和视图函数中的request是一样的,response是视图函数返回的HttpResponse对象。该方法的返回值也必须是HttpResponse对象。

例如,在每一个视图函数的响应头中添加一些东西,就可以在process_response方法中添加响应头

def process_response(self,request,response):                        
'''

:param request:
:param response: 就是视图函数的返回值(HttpResonse对象)
:return:
'''

'''
print(response) response返回的是一个HttpResponse对象
< HttpResponse status_code = 200, "text/html; charset=utf-8" >
'''

#必须返回response这个值,不返回下一个中间件的respnse方法拿不到这个值
return response

4.3 process_view(self, request, view_func, view_args, view_kwargs)

process_view是在视图函数执行前执行

该方法有四个参数

  • request是HttpRequest对象。
  • view_func是Django即将使用的视图函数。(它是实际的函数对象,而不是函数的名称作为字符串。)
  • view_args是将传递给视图的位置参数的列表.
  • view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。

Django会在调用视图函数之前调用process_view方法。

它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后再执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用对应的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。

process_view执行顺序示意图

iShot_2024-08-30_16.43.58

中间件文件

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect,render,HttpResponse

class MD1(MiddlewareMixin):
def process_request(self,request):
print('MD1的process_request方法')

def process_response(self,request,response):
print('MD1的process_response方法')
return response

def process_view(self,request,view_func,view_args,view_kwargs):
print('MD1的process_view方法',view_func.__name__)


class MD2(MiddlewareMixin):
def process_request(self, request):
print('MD2的process_request方法')

def process_response(self, request, response):
print('MD2的process_response方法')
return response

def process_view(self, request, view_func, view_args, view_kwargs):
print('MD2的process_view方法', view_func.__name__)

配置settings文件,MIDDLEWARE项

'app01.mymiddleware.xx.MD1',
'app01.mymiddleware.xx.MD2',

视图文件

def login(request):
#这里打印一个login视图函数是为了看到执行的顺序,process_request,process_response,process_view
print('login视图函数')
if request.method == 'GET':
return render(request,'login.html')

打印结果

iShot_2024-08-30_16.45.06

可以看到,先执行process_request方法,然后执行process_view方法,process_view方法是在视图函数执行前执行,之后执行视图函数,最后执行process_response方法,并且process_response是倒序执行

⚠️⚠️⚠️如果在process_request方法中返回了HttpResponse对象,则只执行自己中间件的process_request方法和process_response方法,如果自己的中间件没有定义process_response方法,则会交给自己类的上一个中间件,如果上一个类也没有,会一直往上推

class MD1(MiddlewareMixin):
def process_request(self,request):
print('MD1的process_request方法')

#这里返回了一个HttpResponse对象,则只执行自己中间件的process_request方法和process_response方法
return HttpResponse('xxx')

def process_response(self,request,response):
print('MD1的process_response方法')
return response

def process_view(self,request,view_func,view_args,view_kwargs):
print('MD1的process_view方法',view_func.__name__)

打印结果

MD1的process_request方法
MD1的process_response方法

示意图

iShot_2024-08-30_16.47.17

4.4 process_exception(self, request, exception)

该方法两个参数:

  • 一个HttpRequest对象
  • 一个exception是视图函数异常产生的Exception对象。

这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。

process_exception执行示意图

iShot_2024-08-30_16.48.21

中间件文件

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect,render,HttpResponse

class MD1(MiddlewareMixin):
def process_request(self,request):
print('MD1的process_request方法')
# return HttpResponse('xxx')

def process_response(self,request,response):
print('MD1的process_response方法')
return response

def process_view(self,request,view_func,view_args,view_kwargs):
print('MD1的process_view方法',view_func.__name__)

def process_exception(self, request, exception):
print(exception)
print("MD1的process_exception方法")
return HttpResponse(str(exception))

class MD2(MiddlewareMixin):
def process_request(self, request):
print('MD2的process_request方法')

def process_response(self, request, response):
print('MD2的process_response方法')
return response

def process_view(self, request, view_func, view_args, view_kwargs):
print('MD2的process_view方法', view_func.__name__)

def process_exception(self, request, exception):
print(exception)
print("MD2的process_exception方法")
return HttpResponse(str(exception))

视图函数文件,在视图函数中抛出一个异常

def login(request):
# print('login视图函数')
print("app01中的login视图函数")
raise ValueError("视图函数中出错了才执行process_exception方法")
return HttpResponse("OK")
if request.method == 'GET':
return render(request,'login.html')

else:
username = request.POST.get('username')
password = request.POST.get('password')

if username == 'admin' and password == 'admin':

request.session['is_login'] = True
request.session['username'] = username

"""
设置session后做的3件事
1 生成随机字符串
2 放到cookie中进行传输
3 将随机字符串和对应数据保存到自己服务端的数据库中
"""

return redirect('home')
else:
return redirect('login')

打印结果,当md1和md2中的process_exception方法都返回了Httpresponse对象,只执行md2的process_exception方法

iShot_2024-08-30_16.49.33

当只有md1返回Httpresponse对象时,md1和md2的process_exception方法都执行

iShot_2024-08-30_16.50.35

4.5 process_template_response(self, request, response)

它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)

process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)

中间件文件

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect,render,HttpResponse

class MD1(MiddlewareMixin):
def process_request(self,request):
print('MD1的process_request方法')
# return HttpResponse('xxx')

def process_response(self,request,response):
print('MD1的process_response方法')
return response

def process_view(self,request,view_func,view_args,view_kwargs):
print('MD1的process_view方法',view_func.__name__)

def process_exception(self, request, exception):
print(exception)
print("MD1的process_exception方法")
return HttpResponse(str(exception))

def process_template_response(self, request, response):
print("MD1的process_template_response方法")
return response

class MD2(MiddlewareMixin):
def process_request(self, request):
print('MD2的process_request方法')

def process_response(self, request, response):
print('MD2的process_response方法')
return response

def process_view(self, request, view_func, view_args, view_kwargs):
print('MD2的process_view方法', view_func.__name__)

def process_exception(self, request, exception):
print(exception)
print("MD2的process_exception方法")
# return HttpResponse(str(exception))

def process_template_response(self, request, response):
print("MD2的process_template_response方法")
return response

视图文件,必须这么写才行

def login(request):
def render():
print("in index/render")
return HttpResponse("OK") # 返回的将是这个新的对象
rep = HttpResponse("OK")
rep.render = render
return rep

打印结果

iShot_2024-08-30_16.52.04

5.中间件执行流程

请求到达中间件之后,先按照正序执行每个注册中间件的process_reques方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpResponse对象,不再执行后面的process_request方法,而是执行当前对应中间件的process_response方法,将HttpResponse对象返回给浏览器。也就是说:如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4,5,6中间件的process_request和process_response方法都不执行,顺序执行3,2,1中间件的process_response方法

iShot_2024-08-30_16.53.45

process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,所有process_view方法执行完后执行视图函数。加入中间件3 的process_view方法返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不执行,直接从最后一个中间件,也就是中间件6的process_response方法开始倒序执行

iShot_2024-08-30_16.55.46

process_template_response和process_exception两个方法的触发是有条件的,执行顺序也是倒序。总结所有的执行流程如下

iShot_2024-08-30_16.56.58