1-Django - 中间件
before
示例运行环境:
django3.2 + python3.9 + win10 + sqlite3 + pycharm
project name:demo
app name: app01
django请求的生命周期
本篇主要围绕下图中的中间件(middleware)部分展开学习。
如上图,所谓的中间件,就是在整个django请求和响应之间的特定节点实现的一组组的框架级别的、轻量级的全局钩子,并且在全局上改变django的输入与输出,因为是全局级别,使用时需要谨慎,以免影响性能。
从我们学习django的开始,从django项目运行起来之后,中间件就一直在默默的起作用,下面就是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'
]
中间件本质上就是一个类,而django又给我们提供了5个不同功能的钩子方法,这样,我们编写一个自定义类,然后重写指定的方法, 就写好了一个自定义的中间件,然后想要该自定义中间件生效的话,还要注册到配置文件中的MIDDLEWARE列表中,该列表中存放的是各个中间件类所在的路径,请求和响应会依次穿过这些中间件。
接下来,一起来看看这五大钩子方法吧。
五大方法
首先把项目各个部分的代码贴一贴。
urls.py
:
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index)
]
views.py
:
from django.shortcuts import render, HttpResponse, redirect
def index(request):
""" 主页 """
print('视图函数 index 执行啦') # 这一行打印主要测试后续的方法谁先执行谁后执行的
return HttpResponse("index page")
setttings.py
,目前MIDDLEWARE列表还是都是默认的中间件,后续我们自定义的中间件也会放到该列表中:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01' # 别忘了注册app
]
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'
]
五大方法小结
学习每个方法之前,先看看这里的总结,更加好理解。
PS:前两个方法用的最多!
1. process_request
- 执行时间节点:在视图函数执行之前执行。
- 多个中间件执行顺序:按照注册顺序,顺序执行。
- 参数:
- request,这个request和视图中的request是同一个。
- 返回值:
- 如果返回None,也就是不写返回值,就表示正常执行完该中间件,然后请求往后继续。
- 如果返回一个render、redirect、HttpResponse或者其他对象,当前中间件后面的中间件的process_request和process_response方法、视图函数都不执行,但会执行当前中间件的process_response方法以及该中间前之前的中间件的process_response方法。
- 重要程度:五星,用的较多。
2. process_response
- 执行时间节点:在视图函数执行之后。
- 多个中间件执行顺序:按照注册顺序,倒叙执行。
- 参数:
- request:和视图函数中的request是同一个对象。
- response:视图函数返回的response对象。
- 返回值:必须返回,否则报错:AttributeError: 'NoneType' object has no attribute 'get'
- 返回视图函数返回的response,即根据需要对视图函数返回的response经过处理后,再返回。
- 返回新的response,用新的response对象,替代视图函数返回的response。
- 重要程度:4星,用的也较多。
3. process_view
- 执行时间节点:路由匹配之后,视图函数执行之前。
- 多个中间件执行顺序:按照注册顺序,顺序执行。
- 参数:
- request:和视图函数中的request是同一个对象。
- view_func:视图函数。
- view_args:视图函数接收的普通位置参数。
- view_kwargs:视图函数接收的关键字参数。
- 返回值:
- 返回None,正常返回视图函数返回的response。
- 或者返回新的response对象。
- 重要程度:3星,用的不多。
4. process_exception
- 执行时间节点:视图函数执行之后,process_resopnse执行之前。
- 触发条件:必须在视图函数报错后,才执行。
- 多个中间件执行顺序:按照注册顺序,倒叙执行。
- 参数:
- request:和视图函数中的request是同一个对象。
- exception:错误信息对象。
- 返回值:
- 返回None,正常返回视图函数返回的response。
- 或者返回新的response对象。
- 重要程度:2星,用的不多。
5. process_template_response
- 执行时间节点:视图函数执行后,process_response之前。
- 触发条件:视图函数返回的response对象必须具有render方法。
- 多个中间件执行顺序:按照注册顺序,倒叙执行。
- 参数:
- request:和视图函数中的request是同一个对象。
- response:视图函数返回的response对象。
- 返回值:必须返回response对象,否则报错
xxxxmiddleware.process_template_response didn't return an HttpResponse object. It returned None instead.
- 重要程度:1星,用的很少。
process_request
一般自定义的中间件都会放在各自的app下面的包内,我们这里为了简单,直接在app下创建中间件文件了。
views.py
:
from django.shortcuts import render, HttpResponse, redirect
def index(request):
""" 主页 """
print('视图函数index:', id(request))
return HttpResponse("index page")
app01\CustomMiddleware.py
:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
"""
自定义的中间件类必须继承MiddlewareMixin
"""
def process_request(self, request):
"""
处理请求的方法
处理完之后,如果返回None,则请求继续往后走
:param request:这个request参数是wsgi封装好的request对象,在当前方法中
:return:
"""
print('CustomMiddleware1.precess_request:', id(request))
# 如果在当前方法中返回请求对象,那么请求就直接被返回了,后续的中间件和视图都不走了
# return HttpResponse("CustomMiddleware1.precess_request")
class CustomMiddleware2(MiddlewareMixin):
"""
自定义的中间件类必须继承MiddlewareMixin
"""
def process_request(self, request):
"""
处理请求的方法,这个request参数是wsgi封装好的request对象,在当前方法中
处理完之后,如果返回None,则请求继续往后走
:param request:
:return:
"""
print('CustomMiddleware2.precess_request:', id(request))
settings.py
:
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',
'app01.CustomMiddleware.CustomMiddleware1', # 自定义的中间件
'app01.CustomMiddleware.CustomMiddleware2', # 自定义的中间件
]
如果浏览器访问index路由的话,就会发现两个中间件被触发执行了,并且在视图之前执行:
CustomMiddleware1.precess_request: 1674878170832
CustomMiddleware2.precess_request: 1674878170832
视图函数index: 1674878170832
[08/Jan/2022 10:03:19] "GET /index/ HTTP/1.1" 200 10
由打印的id结果可以看到,中间件中的request和视图函数中的request对象是同一个;而且经过和中间件的流程是从前到后依次经过中间件。
process_responose
views.py
:
from django.shortcuts import render, HttpResponse, redirect
def index(request):
""" 主页 """
response = HttpResponse("index page")
print('视图函数index, request', id(request), 'response', id(response))
return response
app01\CustomMiddleware.py
:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
"""
自定义的中间件类必须继承MiddlewareMixin
"""
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
return None
def process_response(self, request, response):
"""
在请求经过视图函数处理后,由视图函数返回的response,经过该方法,可以进一步对响应对象进行处理
但是处理之后,该方法必须返回response对象,否则报错:AttributeError: 'NoneType' object has no attribute 'get'
:param request: 和视图函数中的request是同一个对象
:param response: 视图函数返回的response对象
:return: response
"""
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
# 必须返回response
return response
class CustomMiddleware2(MiddlewareMixin):
"""
自定义的中间件类必须继承MiddlewareMixin
"""
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
settings.py
不变:
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',
'app01.CustomMiddleware.CustomMiddleware1', # 自定义用户认证中间件
'app01.CustomMiddleware.CustomMiddleware2', # 自定义用户认证中间件
]
浏览器访问index路由,打印结果:
CustomMiddleware1.precess_request: 1328954751824
CustomMiddleware2.precess_request: 1328954751824
视图函数index, request 1328954751824 response 1328954751200
CustomMiddleware2.process_response, request: 1328954751824 response: 1328954751200
CustomMiddleware1.process_response, request: 1328954751824 response: 1328954751200
两个precess_request顺序执行,在执行视图函数,然后两个process_response倒叙执行,如下图所示:
其他情况我们也来看看。
如果这两个中间件是这样的,且在MIDDLEWARE列表中的位置不变:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
# 必须返回response是视图处理的response
return response
class CustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
return HttpResponse("CustomMiddleware1.precess_request")
# return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
那请求的执行流程是这样的:
CustomMiddleware1.precess_request: 2481922657536
CustomMiddleware2.precess_request: 2481922657536
CustomMiddleware2.process_response, request: 2481922657536 response: 2481922655760
CustomMiddleware1.process_response, request: 2481922657536 response: 2481922655760
请求会直接从当前中间件类的precess_request方法,经过当前中间件类的process_response方法,通过当前中间件类的前一个中间件类的process_response方法返回给客户端,不再经过视图函数处理了。
如果这两个中间件是这样的,且在MIDDLEWARE列表中的位置不变:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
return HttpResponse("CustomMiddleware1.precess_request")
# return None
def process_response(self, request, response):
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
return response
class CustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
请求的执行流程是这样的:
CustomMiddleware1.precess_request: 1901241286464
CustomMiddleware1.process_response, request: 1901241286464 response: 1901241282864
请求会直接经过当前中间件类的process_response方法直接返回,当前中间件后面的中间件的process_request和process_response方法、视图函数都不执行,但会执行当前中间件的process_response方法以及该中间前之前的中间件的process_response方法。
其他情况就不再一一列举了,你可以自己尝试运行查找运行规律。
process_view
settings.py
和urls.py
代码不变。
views.py
:
from django.shortcuts import render, HttpResponse, redirect
def index(request):
""" 主页 """
response = HttpResponse("index page")
print('视图函数index, request', id(request), 'response', id(response))
return response
app01\CustomMiddleware.py
:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
"""
在经过路由匹配之后,视图函数执行之前执行该方法,主要对视图进行相关处理
:param request:和视图函数中的request是同一个对象
:param view_func:视图函数
:param view_args:视图函数接收的普通位置参数
:param view_kwargs:视图函数接收的关键字参数
:return:
返回None,正常返回视图函数返回的response
或者新的response对象
"""
print('CustomMiddleware1.process_view, request: {}, view_func: {}, view_args: {}, view_kwargs: {}'.format(
id(request), id(view_func), view_args, view_kwargs
))
return None
class CustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print(
'CustomMiddleware2.process_view, request: {}, view_func: {}, view_args: {}, view_kwargs: {}'.format(
id(request), id(view_func), view_args, view_kwargs
))
return None
浏览器访问index路由,打印结果:
CustomMiddleware1.precess_request: 2131577016960
CustomMiddleware2.precess_request: 2131577016960
CustomMiddleware1.process_view, request: 2131577016960, view_func: 2131575945824, view_args: (), view_kwargs: {}
CustomMiddleware2.process_view, request: 2131577016960, view_func: 2131575945824, view_args: (), view_kwargs: {}
视图函数index, request 2131577016960 response 2131576350464
CustomMiddleware2.process_response, request: 2131577016960 response: 2131576350464
CustomMiddleware1.process_response, request: 2131577016960 response: 2131576350464
执行流程如下图:
至于view_args和view_kwargs这两个参数:
# 如果urls.py路由是这样的:
re_path('index/(\d+)/', views.index),
# views.py:
def index(request, num):
return HttpResponse("index page")
# 前端访问:
http://127.0.0.1:8000/index/123/
# process_view打印的结果:
view_args: ('123',), view_kwargs: {}
# ---------------------------------------------
# 如果urls.py路由是这样的:
re_path('index/(?P<num>\d+)/', views.index),
# views.py:
def index(request, num):
return HttpResponse("index page")
# 前端访问:
http://127.0.0.1:8000/index/123/
# process_view打印的结果:
view_args: (), view_kwargs: {'num': '123'}
如果这两个中间件是这样的,且在MIDDLEWARE列表中的位置不变:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware1.process_view')
# return None
return HttpResponse("CustomMiddleware1.process_view")
class CustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware2.process_view')
return None
请求的执行流程是这样的:
CustomMiddleware1.precess_request: 2792290605712
CustomMiddleware2.precess_request: 2792290605712
CustomMiddleware1.process_view
CustomMiddleware2.process_response, request: 2792290605712 response: 2792290776784
CustomMiddleware1.process_response, request: 2792290605712 response: 2792290776784
执行流程如下图:
如果这两个中间件是这样的,且在MIDDLEWARE列表中的位置不变:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware1.process_view')
return None
# return HttpResponse("CustomMiddleware1.process_view")
class CustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware2.process_view')
# return None
return HttpResponse("CustomMiddleware2.process_view")
请求的执行流程是这样的:
CustomMiddleware1.precess_request: 1886439673664
CustomMiddleware2.precess_request: 1886439673664
CustomMiddleware1.process_view
CustomMiddleware2.process_view
CustomMiddleware2.process_response, request: 1886439673664 response: 1886439671888
CustomMiddleware1.process_response, request: 1886439673664 response: 1886439671888
执行流程如下图:
process_exception
这个方法,需要注意的是:
- 如果视图函数中没有错误,则所有的process_exception都不执行;如果某个视图函数中出现了错误,则所有的process_exception方法倒叙执行。
- 只有当视图函数中有异常时process_exception才执行,其他中间件方法中如果出现问题,process_exception方法不管,即不执行。
settings.py
不变,urls.py
也不变。
views.py
,故意搞出来一个错误:
from django.shortcuts import render, HttpResponse, redirect
def index(request):
""" 主页 """
response = HttpResponse("index page")
print('视图函数index, request', id(request), 'response', id(response))
# 手动搞出来一个错误
int("xxoo")
return response
app01\CustomMiddleware.py
:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware1.process_view')
return None
# return HttpResponse("CustomMiddleware1.process_view")
def process_exception(self, request, exception):
"""
只有请求期间出现错误才执行该方法,并且该方法捕获错误信息,可以给前端返回
:param request: 根视图函数的request对象是同一个
:param exception: 错误信息对象
:return:
None,正常给前端抛出异常
或者返回一个新的response对象
"""
print('CustomMiddleware1.process_exception, exception: ', exception)
return None
class CustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
# 在这里模拟错误,人家process_exception方法压根不管
# int('xxoo')
return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware2.process_view')
return None
# return HttpResponse("CustomMiddleware2.process_view")
def process_exception(self, request, exception):
print('CustomMiddleware2.process_exception, exception: ', exception)
return None
浏览器访问index路由,打印结果:
CustomMiddleware1.precess_request: 3188306227152
CustomMiddleware2.precess_request: 3188306227152
CustomMiddleware1.process_view
CustomMiddleware2.process_view
视图函数index, request 3188306227152 response 3188305688896
CustomMiddleware2.process_exception, exception: invalid literal for int() with base 10: 'xxoo'
CustomMiddleware1.process_exception, exception: invalid literal for int() with base 10: 'xxoo'
Internal Server Error: /index/
Traceback (most recent call last):
............
File "D:\tmp\demo\app01\views.py", line 25, in index
int("xxoo")
ValueError: invalid literal for int() with base 10: 'xxoo'
[08/Jan/2022 15:31:49] "GET /index/ HTTP/1.1" 500 69184
CustomMiddleware2.process_response, request: 3188306227152 response: 3188306127312
CustomMiddleware1.process_response, request: 3188306227152 response: 3188306127312
执行流程如下图:
如果在第一个中间件类中的process_exception方法中返回了新的response对象,那么:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware1.process_view')
return None
# return HttpResponse("CustomMiddleware1.process_view")
def process_exception(self, request, exception):
print('CustomMiddleware1.process_exception, exception: ', exception)
# return None
return HttpResponse(str(exception))
class CustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
# 在这里模拟错误,人家process_exception方法压根不管
# int('xxoo')
return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware2.process_view')
return None
# return HttpResponse("CustomMiddleware2.process_view")
def process_exception(self, request, exception):
print('CustomMiddleware2.process_exception, exception: ', exception)
return None
请求的执行流程是这样的,并且前端页面不会报错,而是展示的是process_exception方法返回的response内容。
CustomMiddleware1.precess_request: 2706436998288
CustomMiddleware2.precess_request: 2706436998288
CustomMiddleware1.process_view
CustomMiddleware2.process_view
视图函数index, request 2706436998288 response 2706435990480
CustomMiddleware2.process_exception, exception: invalid literal for int() with base 10: 'xxoo'
CustomMiddleware1.process_exception, exception: invalid literal for int() with base 10: 'xxoo'
CustomMiddleware2.process_response, request: 2706436998288 response: 2706437775904
CustomMiddleware1.process_response, request: 2706436998288 response: 2706437775904
执行流程也不变。
如果在第二个中间件类中的process_exception方法中返回了新的response对象,第一个中间件类的process_exception方法中返回None,那么,执行流程就有点变化了:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware1.process_view')
return None
# return HttpResponse("CustomMiddleware1.process_view")
def process_exception(self, request, exception):
print('CustomMiddleware1.process_exception, exception: ', exception)
return None
# return HttpResponse(str(exception))
class CustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
# 在这里模拟错误,人家process_exception方法压根不管
# int('xxoo')
return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware2.process_view')
return None
# return HttpResponse("CustomMiddleware2.process_view")
def process_exception(self, request, exception):
print('CustomMiddleware2.process_exception, exception: ', exception)
# return None
return HttpResponse(str(exception))
请求的执行流程是这样的,前端页面不会报错。
CustomMiddleware1.precess_request: 1945433082224
CustomMiddleware2.precess_request: 1945433082224
CustomMiddleware1.process_view
CustomMiddleware2.process_view
视图函数index, request 1945433082224 response 1945433083376
CustomMiddleware2.process_exception, exception: invalid literal for int() with base 10: 'xxoo'
CustomMiddleware2.process_response, request: 1945433082224 response: 1945433081936
CustomMiddleware1.process_response, request: 1945433082224 response: 1945433081936
当第二个中间件类的process_exception方法返回新的response对象时,请求会直接跳过它前面的process_exception方法,进而穿过来后面的process_response方法返回给前端。
process_template_response
这个方法用的很少,我就没有深入研究,就知道返回时,视图函数的返回的response对象必须具有render方法,这个render和返回页面的render函数不一样!
能干啥,我也没太看出来,这里就就简单看下怎么配置,以及什么时候执行就完了。
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1>index page</h1>
</body>
</html>
views.py
:
from django.shortcuts import render, HttpResponse, redirect
from django.template import loader
def index(request):
""" 主页 """
response = HttpResponse("index page")
print('视图函数index, request', id(request), 'response', id(response))
def render():
""" 魔改的render函数,返回一个html页面"""
content = loader.render_to_string(template_name='index.html', context=None, request=request, using=None)
return HttpResponse(content=content, content_type=None, status=None)
response.render = render
return response
app01\CustomMiddleware.py
:
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class CustomMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware1.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware1.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware1.process_view')
return None
# return HttpResponse("CustomMiddleware1.process_view")
def process_exception(self, request, exception):
print('CustomMiddleware1.process_exception, exception: ', exception)
return None
# return HttpResponse(str(exception))
def process_template_response(self, request, response):
print("CustomMiddleware1.process_template_response,request: ", id(request), "response: ", id(response))
return response
class CustomMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('CustomMiddleware2.precess_request:', id(request))
# return HttpResponse("CustomMiddleware1.precess_request")
return None
def process_response(self, request, response):
print('CustomMiddleware2.process_response, request:', id(request), 'response:', id(response))
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('CustomMiddleware2.process_view')
return None
# return HttpResponse("CustomMiddleware2.process_view")
def process_exception(self, request, exception):
print('CustomMiddleware2.process_exception, exception: ', exception)
return None
# return HttpResponse(str(exception))
def process_template_response(self, request, response):
print("CustomMiddleware2.process_template_response,request: ", id(request), "response: ", id(response))
return response
浏览器访问index路由,打印结果:
CustomMiddleware1.precess_request: 1763286163856
CustomMiddleware2.precess_request: 1763286163856
CustomMiddleware1.process_view
CustomMiddleware2.process_view
视图函数index, request 1763286163856 response 1763285305856
CustomMiddleware2.process_template_response,request: 1763286163856 response: 1763285305856
CustomMiddleware1.process_template_response,request: 1763286163856 response: 1763285305856
CustomMiddleware2.process_response, request: 1763286163856 response: 1763286709584
CustomMiddleware1.process_response, request: 1763286163856 response: 1763286709584
自定义应用
中间件实现用户认证
我们可以在中间件中干很多事情,比如做用户认证。下面的示例演示了只有登录成功或者处于白名单中的url才能被中间件放行,其余的请求都要在中间件中进行session校验。
上代码,urls.py
:
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
path('pay/', views.pay),
path('login/', views.login),
path('logout/', views.logout),
]
views.py
:
from django.shortcuts import render, HttpResponse, redirect
from app01 import models
def login(request):
""" 登录认证 """
if request.method == "GET":
return render(request, 'login.html')
else:
user = request.POST.get("user")
pwd = request.POST.get("pwd")
# print(user, pwd)
obj = models.User.objects.filter(user=user, pwd=pwd).first()
if obj:
request.session['is_login'] = True
request.session['user'] = obj.user
return redirect('/index/')
else:
return render(request, 'login.html', {"msg": "user or password error!!!"})
def index(request):
""" 主页 """
return HttpResponse(f"欢迎用户[{request.session['user']}]访问index页面!!!")
def pay(request):
""" pay页 """
return HttpResponse(f"欢迎用户[{request.session['user']}]访问pay页面!!!")
def logout(request):
""" 清除session,做中间件功能是否完备用的 """
request.session.clear()
return redirect('/login/')
login.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<input type="text" placeholder="用户名" name="user">
<input type="password" placeholder="密码" name="pwd">
<input type="submit" value="提交"><span style="color: red;">{{ msg }}</span>
</form>
</body>
</html>
models.py
:
from django.db import models
class User(models.Model):
user = models.CharField(max_length=32, verbose_name='用户名')
pwd = models.CharField(max_length=64, verbose_name='密码')
def __str__(self):
return self.user
自己执行数据迁移,并且手动录入一些用户信息。
中间件app01\CustomMiddleware.py
:
from django.shortcuts import redirect
from django.utils.deprecation import MiddlewareMixin
class CustomAuthMiddleware(MiddlewareMixin):
# 白名单
white_url_list = ['/login/', '/register/']
def process_request(self, request):
""" 处理请求的中间件方法 """
current_path = request.path
if current_path in self.white_url_list:
return None
# 白名单里面的路径都放行,其他的都要进行session校验
status = request.session.get('is_login', None)
if not status:
return redirect('/login/')
然后在settings.py
中注册中间件,其他配置都是默认的。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01', # 别忘了注册app
]
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',
'app01.CustomMiddleware.CustomAuthMiddleware', # 自定义用户认证中间件
]
然后浏览器可以访问login页面登录后,再访问index或者pay路由都可以了,也可以访问logout路由,退出登录状态,删除session,然后你再访问index或者pay路由时,中间件认证不通过,就会自动跳转到登录页面。
中间件实现自定义限流
示例还是上面的示例,只不过新加一个中间件而已。
中间件app01\CustomMiddleware.py
:
import time
from django.shortcuts import redirect, HttpResponse
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
class CustomAuthMiddleware(MiddlewareMixin):
# 白名单
white_url_list = ['/login/', '/register/']
def process_request(self, request):
""" 处理请求的中间件方法 """
current_path = request.path
if current_path in self.white_url_list:
return None
# 白名单里面的路径都放行,其他的都要进行session校验
status = request.session.get('is_login', None)
if not status:
return redirect('/login/')
class Throttle(MiddlewareMixin):
""" 限制访问ip访问次数 """
def process_request(self, request):
"""
限制每个ip在单位时间内的访问次数
:param request:
:return:
"""
# 1. 获取请求的ip
ip = request.META.get("REMOTE_ADDR")
now = time.time()
# 如果当前ip不在限流字典中,就添加进去
if not settings.THROTTLE_VISIT_DICT.get(ip):
settings.THROTTLE_VISIT_DICT[ip] = []
# 获取当前ip的限流时间戳列表
# {'127.0.0.1': [1641561087.420252, 1641561086.9668493, 1641561086.5121875, 1641561085.9309537]}
# history: [1641561087.420252, 1641561086.9668493, 1641561086.5121875, 1641561085.9309537]
history = settings.THROTTLE_VISIT_DICT[ip]
# 将列表中无效的时间戳删除
# 两个条件:
# 1. history列表必须有值
# 2. 从列表右边往左边挨个判断,如果当前时间戳减去列表中的时间戳大于设定的限流秒数
# 就没啥用了,可以删除了
while history and now - history[-1] > settings.THROTTLE_SECONDS:
history.pop() # pop默认删除列表最后面的元素
# 在单位限流时间内,限制访问次数
if len(history) >= settings.THROTTLE_NUMS:
s = history[-1] + settings.THROTTLE_SECONDS - now
return HttpResponse('<h1 style="text-align:center;color:red;">您的访问过于频繁,请于[{:.2f}]秒后访问</h1>'.format(s))
# 每次请求都将时间戳插入到列表的头部
history.insert(0, now)
print(settings.THROTTLE_VISIT_DICT)
然后在settings.py
中注册中间件,其他配置都是默认的。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01'
]
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',
'app01.CustomMiddleware.CustomMiddleware', # 自定义用户认证中间件
'app01.CustomMiddleware.Throttle', # 自定义限流中间件
]
# ------------- 限流相关 -------------
# 限流时间,单位: 秒
THROTTLE_SECONDS = 10
# 限流次数,单位时间内允许访问的次数
THROTTLE_NUMS = 3
# 用户ip访问的字典
THROTTLE_VISIT_DICT = {
# '127.0.0.1': [1641561025.2066863, 1641561024.730342, 1641561021.7718503]
}
欢迎斧正,that's all,see also: