0

【Django--中间件】

 1 year ago
source link: https://blog.51cto.com/u_15874356/5951562
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、什么是中间件

Django 中间件是是介于request与response处理之间的一道处理过程,是修改 Django request 或者 response 对象的钩子,相对比较轻量级,并且在全局上改变django的输入与输出。
django中间价官网定义:

Middleware is a framework of hooks into Django’s request/response processing.
It’s a light, low-level “plugin” system for globally altering Django’s input or output.

浏览器从请求到响应的过程中,Django 需要通过很多中间件来处理,可以看如下图所示:

【Django--中间件】_中间件

二、中间件的作用

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

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

Django默认的中间件:(在django项目的settings模块中,有一个 MIDDLEWARE变量,其中每一个元素就是一个中间件,如下)

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
# 需要添加在CommonMiddleware中间件之前
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

请求进来是自上而下,通过反射找到类,用for循环来执行,可以自定义中间件,但是也要写在MIDDLEWARE中,可以在app01下创建一个mymid.py文件来写我们自定义的中间件

每一个中间件都有具体的功能

django自带的中间件

中间件可以定义五个方法(主要的是process_request和process_response),分别是:

a.def process_request(self, request):
在请求之前会自动执行
b.def process_view(self, request, callback, callback_args, callback_kwargs):
在执行视图函数或者视图方法之前会自动执行
c.def process_template_response(self, request, response):
在模板渲染之前(前后端不分离)会自动执行
d.def process_response(self, request, response):
在执行视图函数或者视图方法之后会自动执行
e.def process_exception(self, request, exception):
在执行视图函数或者视图方法中间,出现异常时会自动执行

以上方法的返回值可以是None或一个HttpResponse对象,如果是None,则继续按照django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户。

中间件执行过程:

在request请求执行之前,执行process_request中间件,在view视图函数执行之前,执行process_view中间件,如果是前后端不分离的项目模板渲染之前会自动执行process_template_response中间件,前后端分离项目在view视图函数执行之后,会执行process_response中间件,如果在执行视图函数中间出现异常时,会自动执行process_exception中间件。

【Django--中间件】_django_02

三、自定义中间件

1、process_request和process_response

1.1 自定义handle_middilewares.py

我们也可以自己定义一个中间件,自己写一个类,但是必须继承MiddlewareMixin

#file:handle_middilewares.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Md1(MiddlewareMixin):

def process_request(self,request):
print("Md1请求")

def process_response(self,request,response):
print("Md1返回")
return response

class Md2(MiddlewareMixin):

def process_request(self,request):
print("Md2请求")
#return HttpResponse("Md2中断")
def process_response(self,request,response):#
print("Md2返回")
return response
1.2、django中创建一个视图函数或者视图类

此处操作略过,用已有项目演示

1.3、在settings.py的MIDDLEWARE里注册自己定义的中间件
【Django--中间件】_django_03

调用项目的视图函数(调用项目接口),后台打印内容:

Md1请求
Md2请求
此处已调用视图函数
Md2返回
Md1返回
[20/Oct/2020 11:44:49] "GET /projects/?page=1&size=3 HTTP/1.1" 200 965

注意:如果当请求到达请求2的时候直接不符合条件返回,即return HttpResponse("Md2中断"),程序将把请求直接发给中间件2返回,然后依次返回到请求者,不会执行view函数

返回Md2中断的页面,后台打印如下:

Md1请求
Md2请求
Md2返回
Md1返回
[20/Oct/2020 11:47:52] "GET /projects/?page=1&size=3 HTTP/1.1" 200 9

接口调用结果:

【Django--中间件】_中间件_04

流程图如下:

【Django--中间件】_中间件_05

由此总结一下:

  • 中间件的process_request方法是在执行视图函数之前执行的。
  • 当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。
  • 不同中间件之间传递的request都是同一个对象
  • 多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。

2、process_view

process_view(self, request, view_func, view_args, view_kwargs)
该方法有四个参数

  • 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并返回结果。

class Md1(MiddlewareMixin):

def process_request(self,request):
print("Md1请求")
#return HttpResponse("Md1中断")
def process_response(self,request,response):
print("Md1返回")
return response

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md1view")


class Md2(MiddlewareMixin):

def process_request(self,request):
print("Md2请求")
# return HttpResponse("Md2中断")
def process_response(self,request,response):
print("Md2返回")
return response

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md2view")

调用项目的视图函数(调用项目接口),后台打印内容:

Md1请求
Md2请求
Md1view
Md2view
此处已调用视图函数
Md2返回
Md1返回
[20/Oct/2020 14:15:00] "GET /projects/?page=1&size=3 HTTP/1.1" 200 965

下图进行分析上面的过程:

【Django--中间件】_HTTP_06

当最后一个中间的process_request到达路由关系映射之后,返回到中间件1的process_view,然后依次往下,到达views函数,最后通过process_response依次返回到达用户

注意:process_view如果有返回值,会越过其他的process_view以及视图函数,返回process_view的返回值,但是所有的process_response都还会执行。

class Md1(MiddlewareMixin):

def process_request(self,request):
print("Md1请求")
#return HttpResponse("Md1中断")
def process_response(self,request,response):
print("Md1返回")
return response

def process_view(self, request, callback, callback_args, callback_kwargs):

return HttpResponse("hello")

# response=callback(request,*callback_args,**callback_kwargs)
# return response


class Md2(MiddlewareMixin):

def process_request(self,request):
print("Md2请求")

def process_response(self,request,response):
print("Md2返回")
return response

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md2view")

执行结果:

Md1请求
Md2请求
Md2返回
Md1返回
[20/Oct/2020 14:29:33] "GET /projects/?page=1&size=3 HTTP/1.1" 200 5

3、process_exception

process_exception(self, request, exception)

该方法两个参数:

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

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

class Md1(MiddlewareMixin):

def process_request(self,request):
print("Md1请求")
#return HttpResponse("Md1中断")
def process_response(self,request,response):
print("Md1返回")
return response

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md1view")

def process_exception(self, request, exception):
print("md1 process_exception...")
return HttpResponse("VIEW异常")


class Md2(MiddlewareMixin):

def process_request(self,request):
print("Md2请求")

def process_response(self,request,response):
print("Md2返回")
return response

def process_view(self, request, callback, callback_args, callback_kwargs):

print("Md2view")

def process_exception(self, request, exception): # 只有报错了才会执行exception
print("md2 process_exception...")

执行后:

Md1请求
Md2请求
Md1view
Md2view
此处已调用视图函数[20/Oct/2020 14:43:53] "GET /projects/?page=1&size=3 HTTP/1.1" 200 10

md2 process_exception...
md1 process_exception...
Md2返回
Md1返回
【Django--中间件】_django_07

如果第一个process_exception返回一个None(或者没有return),则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
【Django--中间件】_django_08

class Md1(MiddlewareMixin):

def process_request(self,request):
print("Md1请求")
#return HttpResponse("Md1中断")
def process_response(self,request,response):
print("Md1返回")
return response

def process_view(self, request, callback, callback_args, callback_kwargs):
print("Md1view")

def process_exception(self, request, exception):
print("md1 process_exception...")


class Md2(MiddlewareMixin):

def process_request(self,request):
print("Md2请求")

def process_response(self,request,response):
print("Md2返回")
return response

def process_view(self, request, callback, callback_args, callback_kwargs):

print("Md2view")

def process_exception(self, request, exception): # 只有报错了才会执行exception
print("md2 process_exception...")
return HttpResponse("VIEW异常--md2")

执行结果:

Md1请求
Md2请求
Md1view
Md2view
此处已调用视图函数
md2 process_exception...
Md2返回
Md1返回
【Django--中间件】_中间件_09

当views出现错误时,流程图如下:

【Django--中间件】_django_10

4、process_template_response(self,request,response)

该方法对视图函数返回值有要求,必须是一个含有render方法类的对象,才会执行此方法

四 中间件应用场景

1、做IP访问频率限制

某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。

2、URL访问过滤

如果用户访问的是login视图(放过)

如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就不需要在多个视图函数上写验证登录状态的装饰器了!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK