Django处理http请求之中间件

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
云原生网关 MSE Higress,422元/月
性能测试 PTS,5000VUM额度
简介: Django处理http请求之中间件

Django处理http请求之中间件


 

测试环境

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(request, view_func, view_args, view_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(request, exception)

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(request, response)

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

 

目录
相关文章
|
21天前
|
JSON Java 数据格式
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
74 25
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
|
2天前
|
JSON JavaScript 前端开发
什么是HTTP POST请求?初学者指南与示范
HTTP POST请求是一种常用的HTTP方法,主要用于向服务器发送数据。通过合理设置请求头和请求主体,可以实现数据的可靠传输。无论是在客户端使用JavaScript,还是在服务器端使用Node.js,理解和掌握POST请求的工作原理和应用场景,对于Web开发至关重要。
62 18
|
2天前
|
JSON 数据格式
.net HTTP请求类封装
`HttpRequestHelper` 是一个用于简化 HTTP 请求的辅助类,支持发送 GET 和 POST 请求。它使用 `HttpClient` 发起请求,并通过 `Newtonsoft.Json` 处理 JSON 数据。示例展示了如何使用该类发送请求并处理响应。注意事项包括:简单的错误处理、需安装 `Newtonsoft.Json` 依赖,以及建议重用 `HttpClient` 实例以优化性能。
42 2
|
19天前
|
Web App开发 大数据 应用服务中间件
什么是 HTTP Range请求(范围请求)
HTTP Range 请求是一种非常有用的 HTTP 功能,允许客户端请求资源的特定部分,从而提高传输效率和用户体验。通过合理使用 Range 请求,可以实现断点续传、视频流播放和按需加载等功能。了解并掌握 HTTP Range 请求的工作原理和应用场景,对开发高效的网络应用至关重要。
58 15
|
1月前
|
开发者
HTTP 协议请求方法的发展历程
【10月更文挑战第21天】
|
23天前
|
数据采集 JSON 测试技术
Grequests,非常 Nice 的 Python 异步 HTTP 请求神器
在Python开发中,处理HTTP请求至关重要。`grequests`库基于`requests`,支持异步请求,通过`gevent`实现并发,提高性能。本文介绍了`grequests`的安装、基本与高级功能,如GET/POST请求、并发控制等,并探讨其在实际项目中的应用。
33 3
|
28天前
|
前端开发 UED 开发者
CSS Sprites和图标字体在网页图标加载优化中的应用。CSS Sprites通过合并多图标减少HTTP请求,提升加载速度
本文探讨了CSS Sprites和图标字体在网页图标加载优化中的应用。CSS Sprites通过合并多图标减少HTTP请求,提升加载速度;图标字体则以字体形式呈现图标,便于调整样式。文章分析了两者的优缺点及应用场景,并提供了应用技巧和注意事项,旨在帮助开发者提升页面性能,改善用户体验。
24 5
|
1月前
|
缓存 前端开发 API
|
2月前
|
数据采集 前端开发 算法
Python Requests 的高级使用技巧:应对复杂 HTTP 请求场景
本文介绍了如何使用 Python 的 `requests` 库应对复杂的 HTTP 请求场景,包括 Spider Trap(蜘蛛陷阱)、SESSION 访问限制和请求频率限制。通过代理、CSS 类链接数控制、多账号切换和限流算法等技术手段,提高爬虫的稳定性和效率,增强在反爬虫环境中的生存能力。文中提供了详细的代码示例,帮助读者掌握这些高级用法。
128 1
Python Requests 的高级使用技巧:应对复杂 HTTP 请求场景
|
1月前
|
JSON API 数据格式
Python中获取HTTP请求响应体的详解
本文介绍了如何使用Python的`requests`和`urllib`库发送HTTP请求并处理响应体。`requests`库简化了HTTP请求过程,适合快速开发;`urllib`库则更为底层,适用于性能要求较高的场景。文章详细演示了发送GET请求、处理JSON响应等常见操作。
50 3