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. Redis知识网络

    Redis知识网络 作者:运维君莫笑 链接:https://www.zhihu.com/question/470465324/answer/2006650219 Redis为什么这么快? 根据官方数据 ...

  2. Servlet转发与重定向的资源路径问题解析

    一. 问题引入 转发和重定向可以说是Servlet中最重要的知识点也不为过,因为它决定着整个向Servlet体系中,执行流程的问题.      转发: request.getRequestDispat ...

  3. RabbitMQ系列(五) RabbitMQ的文件和目录位置

    概述 每个RabbitMQ节点使用一些文件和目录,用于加载配置.存储数据 / 元数据 / 日志文件等等.这些文件和目录的位置是可以自定义的. 本指南涵盖: 1)如何自定义RabbitMQ节点所使用的各 ...

  4. Keil_MDK中无法打开map文件的解决办法

    如果在MDK中打开map文件 我们在STM32的开发过程中,经常会查看.map文件 .map文件是MDK在编译过程中生成的一个包含镜像文件信息的重要文件,在程序编译后自动生成,比方这里我的工程下自动将 ...

  5. powerpoint 无法打开文件

    PowerPoint 无法打开文件 出现的问题 今天下载老师放在学习通的ppt,居然不能打开,记录一下 点击修复后出现: 这并不是文件损坏了,而是powerpoint出于安全的考虑,为了保护我们的计算 ...

  6. k8s——kubctl命令基础

    语法 kubevtl [command] [type] [name] [flags] command: 指定要对一个或多个资源执行的操作,例如,`create`,`get`,`describe`,`d ...

  7. Vue简单自定义Canvas验证码组件。

    在您的Vue项目中,是否曾遇到过需要增加验证码来增强账户安全性的情况?这个Vue通用Canvas验证码组件!采用Canvas,实现了高度自定义和灵活的验证码生成方式,让您的网站或应用轻松应对各类验证码 ...

  8. 语义化结构标签 多媒体标签 H5新增表单内容

    语义化结构标签: section  更偏向于一个区域类似div(块) article 更偏向于显示内容(块) aside 标签作为article呢绒的辅助板块(块) header 标签做为一个网页头部 ...

  9. 【Java面试题-基础知识01】Java数据类型四连问?

    一.Java中的基础数据类型有哪些? Java中的基本数据类型包括: 1. byte:8位有符号整数,范围为-128到127.2. short:16位有符号整数,范围为-32768到32767.3. ...

  10. Easysearch 容量规划建议

    基于容量估算 主要问题: 每天将索引多少原始数据(GB)?保留数据多少天? 原始数据膨胀率 您将强制执行多少个副本分片? 您将为每个数据节点分配多少内存? 您的内存:数据比例是多少? 原则 保留 +1 ...