Django处理http请求之中间件

by:授客 QQ1033553122 欢迎加入全国软件测试交流QQ群:7156436

测试环境

Win7

Django 1.11

自定义中间件

中间件“工厂”是一个携带一个可调用get_response参数并返回一个中间件的的可调用对象。中间件则是一个携带request参数并返回一个response的可调用对象,正如view视图函数。

中间件可以写成类似如下的函数(假设以下代码位于 my_middleware.py文件中,项目结构如下):

def simple_middleware(get_response):

print('进入中间件')

 

    def middleware(request):

        # 针对每个request,这里的代码,会在view、后续中间件被调用之前执行(Code to be executed for each request before the view (and later      middleware) are called.

response = get_response(request)

        # 针对每个request,这里的代码,会在view、后续中间件被调用之后执行(Code to be executed for each request/response after the view is called

        return response

    return middleware

或者如下,写成一个类,该类的实例为一个可调用对象

class SimpleMiddleware:

    def __init__(self, get_response):

self.get_response = get_response

        # 只配置并初始化一次(one-time configuration and initialization.

    def __call__(self, request):

        # 针对每个request,这里的代码,会在view、后续中间件被调用之前执行(Code to be executed for each request before the view (and later middleware) are called.

response = self.get_response(request)

        # 针对每个request,这里的代码,会在view、后续中间件被调用之前执行(Code to be executed for each request before the view (and later middleware) are called.

        return response

django提供的get_response可能是实际view视图(如果当前中间是list中配置的最后一个中间件)、下一个中间件,当前中间件不需要知道它是啥。

中间件可以放在python path中的任何地方

__init__(get_response)

中间件工厂必须接受一个get_response参数,可以为中间件初始化一些全局状态,但是要注意:

  • Django只允许用get_response初始化中间件,所以__init__()定义不能包含其它任何参数的。
  • __call__()方法不一样,针对每个request__call__()都会被调用一次,而__init__()仅在web 服务器启动时被调用一次(注意:实践表明 setting.py DEBUG = True时,启动服务时,__init__()可能被调用两次)

标记不被使用的中间件

在对应中间件的 __init__() 方法中抛出 MiddlewareNotUsed,Django将会在处理中间件时移除对应的中间件,并在DEBUG设置为True的情况下,往django.request logger中写入一条调试消息。

激活中间件

添加目标中间件到settings.py中的MIDDLEWARE list中以激活中间件,注意新增中间件后需要重启服务器。

MIDDLEWARE中,每个中间件以一个字符串表示:指向中间件工厂类、函数的全python路径。如下:

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',
'website.middleware.my_middleware.simple_middleware',
'website.middleware.my_middleware.SimpleMiddleware',
]

MIDDLEWARE可以配置为空,但是强烈建议至少包含CommonMiddleware

中间件在MIDDLEWARE中的顺序很关键,因为一个中间件可能会依赖另一个中间件。例如 AuthenticationMiddleware在会话中存储已授权用户信息,所以,它必须在SessionMiddleware之后运行所以,自定义中间件建议都放到最后面。See Middleware ordering for some common hints about ordering of Django middleware classes。

中间件顺序和分层

request阶段,view调用之前,Django会按顺序-中间件在MIDDLEWARE中的定义,从上往下(索引从小到大),把中间件作用于request(During the request phase, before calling the view, Django applies middleware in the order it’s defined in MIDDLEWARE, top-down)

可以把它看成一个洋葱:每个中间件类都是一层包裹了view视图(洋葱的核心)的皮,如果请求通过了洋葱所有皮(每层都会调用get_response以便把request传递给下一层),到达核心view,那么将按相反的顺序,把response一层一层的往外传。

如果其中一层短路了,没有调用get_response的情况下,返回了response,该层所包裹的所有层(包括view视图)将看不到当前requestresponseresponse只会经过request已经通过的层。

其它中间件钩子

除了上述描述的基础的request/response中间件模式,还可以添加以下三种特定的方法给基于类的中间件:

process_view()

process_view(requestview_funcview_argsview_kwargs)

request 为一个 HttpRequest 对象。

view_func为Django即将调用的python函数 (实际函数对象,而非表示函数名称的字符串

view_args 传递给view函数的位置参数list列表

view_kwargs 传递给view函数的字典参数,不管是view_args 还是 view_kwargs都不包含第一个参数(request).

process_view() Django调用view之前,__call__()被调用之后被调用,如下:

__call__() ->process_view() -> view function -> __call__()

函数应该返回None或者一个HttpResponse对象。如果返回NoneDjango将继续处理request,执行其它中间件的process_view(),最后执行对应的view。如果返回一个HttpResponse对象,Django将不会调用对应的view及后续的process_exception(), process_template_response()等,直接调用对应的response中间件作用于该response对象并返回结果.

注意:

应该避免在view视图运行之前,在中间件内部访问 request.POST因为这将阻止该中间件之后的任何视图 modify the upload handlers for the request(Accessing request.POST inside middleware before the view runs or in process_view() will prevent any view running after the middleware from being able to modify the upload handlers for the request, and should normally be avoided)

CsrfViewMiddleware类可以被看做一个异常,因为它提供csrf_exempt() 和csrf_protect()装饰器,可以显示控制在哪里进行CSRF校验。 (The CsrfViewMiddleware class can be considered an exception, as it provides the csrf_exempt() andcsrf_protect() decorators which allow views to explicitly control at what point the CSRF validation should occur

process_exception()

process_exception(requestexception)

request 为一个 HttpRequest 对象。

exception 为view视图函数的一个 Exception 对象。

view抛出一个异常时,Django才会调用process_exception()。函数应该返回None或者一个HttpResponse对象。如果返回一个HttpResponse对象,将应用template responseresponse中间件并返回上述描述的HttpResponse对象,结果给浏览器,否则走默认的异常处理(default exception handling 

相反的,response阶段(包括process_exception),按逆序运行中间件。如果异常中间件返回了一个response,位于该中间件前面的中间件(MIDDLEWARElist 中对应索引比当前中间件的索引小的中间件)的process_exception都不会被调用。

 

process_template_response()

process_template_response(requestresponse)

request  为一个 HttpRequest 对象。

response 为Django view、中间件返回的一个TemplateResponse对象

process_template_response() view视图执行完成后才被调用。如果response实例有render()方法,它将被视为TemplateResponse 。形如:

from django.template.response import TemplateResponse

def test_page(request):

return TemplateResponse(request, 'website/pages/mytest.html',{})

它必须返回实现了render方法的response对象。可以通过改变response.template_nameresponse.context_data更改给定response,或者返回一个全新的TemplateResponse

无需显示的渲染response--response将在所有template response中间件调用完成后自动被渲染。

response阶段(包括process_template_response()),按逆序运行中间件。

Dealing with streaming responses

不同于HttpResponse,StreamingHttpResponse沒有content属性,因此中间件不能认为所有的响应都有content 属性,如果想要访问content,需要测试流式响应:

if response.streaming:

response.streaming_content = wrap_streaming_content(response.streaming_content)

else:

response.content = alter_content(response.content)

注意:streaming_content被假定为太大而不能存放在内存中,响应中间件可以将它包裹在一个生成器中,但是不能消费它,通常如下所示:

Def wrap_streaming_content(content):

for chunk in content:

yield alter_content(chunk)

Exception handling

例子1

# -*- coding: utf-8 -*-
 
__author__ = 'shouke'
 
# from django.http import HttpResponse
 
class SimpleMiddleware1:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.
        print('call __init__ in SimpleMiddleware1')
 
    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        print('call __call_ in SimpleMiddleware1 before the view is called')
 
        response = self.get_response(request)
 
       # Code to be executed for each request/response after
        # the view is called.
        print('call __call_ in SimpleMiddleware1 after the view is called')
        return response
 
 
    def process_view(self, request, view_func, view_args, view_kwargs):
        print('call process_view in SimpleMiddleware1')
        # return HttpResponse('shouke')
 
 
    def process_template_response(self, request, response):
        print('call process_template_response in SimpleMiddleware1')
        return response
 
 
    def process_exception(self, request, exception):
        print('call process_exception in SimpleMiddleware1')
 
 
class SimpleMiddleware2:
    def __init__(self, get_response):
       self.get_response = get_response
       # One-time configuration and initialization.
       print('call __init__ in SimpleMiddleware2')
 
    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        print('call __call_ in SimpleMiddleware2 before the view is called')
 
        response = self.get_response(request)
 
        # Code to be executed for each request/response after
        # the view is called.
        print('call __call_ in SimpleMiddleware2 after the view is called')
        return response
 
 
    def process_view(self, request, view_func, view_args, view_kwargs):
        print('call process_view in SimpleMiddleware2')
 
 
    def process_template_response(self, request, response):
        print('call process_template_response in SimpleMiddleware2')
        return response
 
 
    def process_exception(self, request, exception):
        print('call process_exception in SimpleMiddleware2')
        # return HttpResponse('shouke')

 

View函数

def test_page(request):

print('call view function test_page')

# 1/0

return TemplateResponse(request, 'website/pages/mytest.html',{})

中间件配置

MIDDLEWARE = [

……

    'website.middleware.my_middleware.SimpleMiddleware1',

    'website.middleware.my_middleware.SimpleMiddleware2',]

 

运行结果

Upgrading pre-Django 1.10-style middleware

 
From django.utils.deprecation import MiddlewareMixin

 

class MiddlewareMixin(object):
def __init__(self, get_response=None):
     self.get_response = get_response
     super(MiddlewareMixin, self).__init__()
 
def __call__(self, request):
     response = None
     if hasattr(self, 'process_request'):
          response = self.process_request(request)
     if not response:
          response = self.get_response(request)
     if hasattr(self, 'process_response'):
          response = self.process_response(request, response)
     return response

 

Django提供了django.utils.deprecation.MiddlewareMixin来简化中间件类的创建,MiddlewareMixin兼容 MIDDLEWARE 和老版本的 MIDDLEWARE_CLASSES。Django包含的所有中间件类都是兼容彼此的配置的。

如果使用 MIDDLEWARE_CLASSES, 将不会调用__call__;直接调用 process_request() 和process_response()

大多数情况下,直接从MiddlewareMixin继承创建中间件就够了。

使用 MIDDLEWARE 和MIDDLEWARE_CLASSES的区别?

例子2

修改中间件代码如下,其它保持不变
# -*- coding: utf-8 -*-
 
__author__ = 'shouke'
 
from django.utils.deprecation import MiddlewareMixin
# from django.http import HttpResponse
 
class SimpleMiddleware1(MiddlewareMixin):
    def process_request(self, request):
        print('call process_request in SimpleMiddleware1')
 
 
    def process_response(self, request, response):
        print('call process_response in SimpleMiddleware1')
        return response
 
 
 
class SimpleMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('call process_request in SimpleMiddleware2')
 
    def process_response(self, request, response):
        print('call process_response in SimpleMiddleware2')
        return response
 
运行结果

 

说明:

process_request在调用view函数视图之前执行;

Process_response在调用view函数视图之后执行;

参考链接

https://docs.djangoproject.com/en/2.1/topics/http/middleware/

https://docs.djangoproject.com/en/2.1/_modules/django/middleware/common/#CommonMiddleware

Django 处理http请求之中间件的更多相关文章

  1. Django框架10 /sweetalert插件、django事务和锁、中间件、django请求生命周期

    Django框架10 /sweetalert插件.django事务和锁.中间件.django请求生命周期 目录 Django框架10 /sweetalert插件.django事务和锁.中间件.djan ...

  2. django系列8.2--django的中间件流程

    Django请求流程图 请求到达中间件之后,先按照正序执行每个注册中间件的process_reques方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpRe ...

  3. django系列8.1--django的中间件01 自定义中间件的5个方法

    一.Django中的中间件 Django中间件定义: Middleware is a framework of hooks into Django's request/response process ...

  4. Django 补充models操作,中间件, 缓存,信号,分页

    1.Model 一对多 补充 models如下: class UserType(models.Model): caption = models.CharField(max_length=16) cla ...

  5. Django --- cookie与session,中间件

    目录 1.cookie与session 1.cookie 2.session 2.中间件 1.中间件作用 2.用户可以自定义的五个方法 3.自定义中间件 1.cookie与session 1.cook ...

  6. 玩转Django的POST请求 CSRF

    玩转Django的POST请求 CSRF 不少麻油们玩django都会碰到这个问题,POST请求莫名其妙的返回 403 foribidden,希望这篇博文能解答所有问题 三种方法 To enable ...

  7. Django跨域请求之JSONP和CORS

    现在来新建一个Django项目server01,url配置为 url(r'^getData.html$',views.get_data) 其对应的视图函数为get_data: from django. ...

  8. Django添加防跨站请求伪造中间件

    第一步: 在全局设置中打开此中间件: MIDDLEWARE_CLASSES = [ ... 'django.middleware.csrf.CsrfViewMiddleware', ... ]     ...

  9. Django框架(十七)—— 中间件、CSRF跨站请求伪造

    目录 中间件 一.什么是中间件 二.中间件的作用 三.中间件执行顺序 四.自定义中间件 1.导包 2.定义类,继承MiddlewareMixin 3.在视图函数中定义一个函数 4.在settings的 ...

  10. django 启动和请求

    Django运行方式 调试模式 直接 python manage.py runserver python manage.py runserver python manage.py runserver ...

随机推荐

  1. IDEA使用——常用配置

    一. 设置鼠标悬停代码提示 二. 设置Ctrl+滚乱调整字体大小 三. 设置自动导包(不设置自动导包,我们也可以通过Alt+Enter进行手动导包) 四. 设置显示行号和方法间分隔符 五. 设置代码提 ...

  2. 【BI 可视化插件】怎么做? 手把手教你实现

    背景 对于现在的用户来说,插件已经成为一个熟悉的概念.无论是在使用软件. IDE 还是浏览器时,插件都是为了在原有产品基础上提供更多更便利的操作.在 BI 领域,图表的丰富性和对接各种场景的自定义是最 ...

  3. CSS——3D转换

  4. Qt-FFmpeg开发-保存视频流裸流(11)

    音视频/FFmpeg #Qt Qt-FFmpeg开发-保存视频流裸流 目录 音视频/FFmpeg #Qt Qt-FFmpeg开发-保存视频流裸流 1.概述 2.实现效果 3.FFmpeg保存裸流代码流 ...

  5. 新手入门html

    网页的组成:结构 表现 行为 Web标准: 结构  表现 行为 Html  css  js Html和css 是w3c制定标准  js是ECMA制定标准 HTML:指的是超文本标记语言 文件命名的规范 ...

  6. vue组件间传值 父组件向子组件传值

    ² 父组件以属性的形式绑定值到子组件身上 ² 子组件通过使用属性props接收(props是单向绑定的(只读属性):当父组件的属性变化时,将传导给子组件,但是反过来不会) 第1步,父组件以动态属性的方 ...

  7. Xcode 配置账号

    Xcode 配置开发者账号 简介:在iOS 开发过程中,要打包到手机上是需要登陆账号和配置对应证书的. 配置账号 打开Xcode -> Preferences -> Accounts -& ...

  8. ITMS-90717: 无效的应用程序商店图标

    PS导入照片 图像->模式->索引颜色 透明度去掉打勾保存即可

  9. 【UnityTips】如何自定义脚本模版

    [UnityTips]如何自定义脚本模版 通常我们创建新脚本时大家看到的是这个样子: using System.Collections; using System.Collections.Generi ...

  10. WPF/C#:数据绑定到方法

    在WPF Samples中有一个关于数据绑定到方法的Demo,该Demo结构如下: 运行效果如下所示: 来看看是如何实现的. 先来看下MainWindow.xaml中的内容: <Window.R ...