这片文章将讨论下面内容:

1.什么是middleware

2.什么时候使用middleware

3.我们写middleware必须要记住的东西

4.写一些middlewares来理解中间件的工作过程和要点

什么是middleware

Middleware是修改django request 或者 response对象的钩子,下面是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.

什么时候使用middleware

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

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

django提供了一些默认的moddleware,例如:

AuthenticationMiddleware

大家可能频繁在view使用request.user吧。django想在每个view执行之前把user设置为request的属性,于是就用了一个中间件来实现这个目标。所以django提供了可以修改request对象的中间件部分:AuthenticationMiddleware

from django.conf import settings
from django.contrib import auth
from django.contrib.auth import load_backend
from django.contrib.auth.backends import RemoteUserBackend
from django.core.exceptions import ImproperlyConfigured
from django.utils.deprecation import MiddlewareMixin
from django.utils.functional import SimpleLazyObject def get_user(request):
if not hasattr(request, '_cached_user'):
request._cached_user = auth.get_user(request)
return request._cached_user class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request)) class RemoteUserMiddleware(MiddlewareMixin):
"""
Middleware for utilizing Web-server-provided authentication.
If request.user is not authenticated, then this middleware attempts to
authenticate the username passed in the ``REMOTE_USER`` request header.
If authentication is successful, the user is automatically logged in to
persist the user in the session.
The header used is configurable and defaults to ``REMOTE_USER``. Subclass
this class and change the ``header`` attribute if you need to use a
different header.
""" # Name of request header to grab username from. This will be the key as
# used in the request.META dictionary, i.e. the normalization of headers to
# all uppercase and the addition of "HTTP_" prefix apply.
header = "REMOTE_USER"
force_logout_if_no_header = True def process_request(self, request):
# AuthenticationMiddleware is required so that request.user exists.
if not hasattr(request, 'user'):
raise ImproperlyConfigured(
"The Django remote user auth middleware requires the"
" authentication middleware to be installed. Edit your"
" MIDDLEWARE setting to insert"
" 'django.contrib.auth.middleware.AuthenticationMiddleware'"
" before the RemoteUserMiddleware class.")
try:
username = request.META[self.header]
except KeyError:
# If specified header doesn't exist then remove any existing
# authenticated remote-user, or return (leaving request.user set to
# AnonymousUser by the AuthenticationMiddleware).
if self.force_logout_if_no_header and request.user.is_authenticated:
self._remove_invalid_user(request)
return
# If the user is already authenticated and that user is the user we are
# getting passed in the headers, then the correct user is already
# persisted in the session and we don't need to continue.
if request.user.is_authenticated:
if request.user.get_username() == self.clean_username(username, request):
return
else:
# An authenticated user is associated with the request, but
# it does not match the authorized user in the header.
self._remove_invalid_user(request) # We are seeing this user for the first time in this session, attempt
# to authenticate the user.
user = auth.authenticate(request, remote_user=username)
if user:
# User is valid. Set request.user and persist user in the session
# by logging the user in.
request.user = user
auth.login(request, user) def clean_username(self, username, request):
"""
Allow the backend to clean the username, if the backend defines a
clean_username method.
"""
backend_str = request.session[auth.BACKEND_SESSION_KEY]
backend = auth.load_backend(backend_str)
try:
username = backend.clean_username(username)
except AttributeError: # Backend has no clean_username method.
pass
return username def _remove_invalid_user(self, request):
"""
Remove the current authenticated user in the request which is invalid
but only if the user is authenticated via the RemoteUserBackend.
"""
try:
stored_backend = load_backend(request.session.get(auth.BACKEND_SESSION_KEY, ''))
except ImportError:
# backend failed to load
auth.logout(request)
else:
if isinstance(stored_backend, RemoteUserBackend):
auth.logout(request) class PersistentRemoteUserMiddleware(RemoteUserMiddleware):
"""
Middleware for Web-server provided authentication on logon pages.
Like RemoteUserMiddleware but keeps the user authenticated even if
the header (``REMOTE_USER``) is not found in the request. Useful
for setups when the external authentication via ``REMOTE_USER``
is only expected to happen on some "logon" URL and the rest of
the application wants to use Django's authentication mechanism.
"""
force_logout_if_no_header = False

假如你有一个应用,它的用户是不同时区的人。你想让他们在访问任何页面的时候都能显示正确的时区,想让所有的views中都能得到用户自己的timezone信息。这种情况下可以用session来解决,所以你可以像下面添加一个middleware:

class TimezoneMiddleware(object):
def process_request(self, request):
# Assuming user has a OneToOneField to a model called Profile
# And Profile stores the timezone of the User.
request.session['timezone'] = request.user.profile.timezone

TimezoneMiddleware是依赖于request.user的,request.user是通过AuthenticationMiddleware来设置的。所以在settings.MIDDLEWARE_CLASSES配置中,TimezoneMiddleware一定要在AuthenticationMiddleware之后。

下面的例子可以得到关于中间件顺序的更多体会。

使用middleware时应该记住的东西

  middlewares 的顺序非常重要

  一个middleware只需要继承object类

  一个middleware可以实现一些方法并且不需要实现所有的方法

  一个middleware可以实现process_request(方法)但是不可以实现process_respose(方法)和process_view方法。这些都很常见,django提供了很多middlewares可以做到。

  一个midd可以实现process_response 方法,但是不需要实现process_request 方法。

AuthenticationMiddleware只实现了对请求的处理,并没有处理响应。

GZipMiddleware 只实现了对响应的处理,并没有实现对请求和view的处理

import re

from django.utils.cache import patch_vary_headers
from django.utils.deprecation import MiddlewareMixin
from django.utils.text import compress_sequence, compress_string re_accepts_gzip = re.compile(r'\bgzip\b') class GZipMiddleware(MiddlewareMixin):
"""
Compress content if the browser allows gzip compression.
Set the Vary header accordingly, so that caches will base their storage
on the Accept-Encoding header.
"""
def process_response(self, request, response):
# It's not worth attempting to compress really short responses.
if not response.streaming and len(response.content) < 200:
return response # Avoid gzipping if we've already got a content-encoding.
if response.has_header('Content-Encoding'):
return response patch_vary_headers(response, ('Accept-Encoding',)) ae = request.META.get('HTTP_ACCEPT_ENCODING', '')
if not re_accepts_gzip.search(ae):
return response if response.streaming:
# Delete the `Content-Length` header for streaming content, because
# we won't know the compressed size until we stream it.
response.streaming_content = compress_sequence(response.streaming_content)
del response['Content-Length']
else:
# Return the compressed content only if it's actually shorter.
compressed_content = compress_string(response.content)
if len(compressed_content) >= len(response.content):
return response
response.content = compressed_content
response['Content-Length'] = str(len(response.content)) # If there is a strong ETag, make it weak to fulfill the requirements
# of RFC 7232 section-2.1 while also allowing conditional request
# matches on ETags.
etag = response.get('ETag')
if etag and etag.startswith('"'):
response['ETag'] = 'W/' + etag
response['Content-Encoding'] = 'gzip' return response

参照文档

写一些middlewares

首先确认你有一个django项目,需要一个url和view,并且可以进入这个view。下面我们会对request.user做几个测试,确认权限设置好了,并可以在view中正确打印request.user的信息。

在任意一个app中创建middleware.py文件。

我有一个叫做books的app,所以文件的位置是books/middleware.py

class BookMiddleware(object):
def process_request(self,request):
print('Middleware executed')

MIDDLEWARE_CLASSES中添加这个中间件

MIDDLEWARE_CLASSES = (
'books.middleware.BookMiddleware',
'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',
)

对任意的一个url发送请求

Middleware executed

修改BookMiddleware.process_request如下

class BookMiddleware(object):
def process_request(self, request):
print "Middleware executed"
print request.user

再次访问一个url,将会引起一个错误

'WSGIRequest' object has no attribute 'user'

这是因为request对象还没有设置user属性呢。

现在我们改变下middlewares的顺序,BookMiddleware 放在 AuthenticationMiddleware之后

MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'books.middleware.BookMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

访问一个url,runserver控制台打印如下

Middleware executed
<username>

这说明middlewares处理request的顺序跟settings.MIDDLEWARE_CLASSES中列出的顺序是一致的。

可以进一步证实,middleware.py 添加另外一个middleware

class AnotherMiddleware(object):
def process_request(self, request):
print "Another middleware executed"

把他也加到MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'books.middleware.BookMiddleware',
'books.middleware.AnotherMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

现在的输出是:

Middleware executed
<username>
Another middleware executed

在process_request方法中返回HttpResponse,把BookMilldeware改成下面这样:

class BookMiddleware(object):
def process_request(self, request):
print "Middleware executed"
print request.user
return HttpResponse("some response")

尝试下任何一个url,会得到如下输出:

Middleware executed
<username>

你会注意到下面两个事情:

  不管你访问哪个url,自己写的view处理方法都不执行了,只有“some response” 这样一种响应。

  AnotherMiddleware.process_request不再被执行。

所以如果Middleware的process_request方法中返回了HttpResponse对象,那么它之后的中间件将被略过,view中的处理方法也被略过。

所以在实际的项目中很少会这么干(不过也有些项目会,比如做代理)

注释掉 "return HttpResponse("some response")" ,两个middleware才能正常的处理请求。

使用process_response

给这两个middleware添加process_response方法

class AnotherMiddleware(object):
def process_request(self, request):
print "Another middleware executed" def process_response(self, request, response):
print "AnotherMiddleware process_response executed"
return response class BookMiddleware(object):
def process_request(self, request):
print "Middleware executed"
print request.user
return HttpResponse("some response")
#self._start = time.time() def process_response(self, request, response):
print "BookMiddleware process_response executed"
return response

访问一些url,得到如下的输出

Middleware executed
<username>
Another middleware executed
AnotherMiddleware process_response executed
BookMiddleware process_response executed

AnotherMiddleware.process_response() 在BookMiddleware.process_response() 之前执行 而 AnotherMiddleware.process_request() 在BookMiddleware.process_request() 之后执行. 所以process_response()  执行的顺序跟 process_request正好相反. process_response() 执行的顺序是从最后一个中间件执行,到倒数第二个,然后直到第一个中间件.

process_view

django按顺序执行中间件process_view()的方法,从上到下。类似process_request方法执行的顺序。

所以如果任何一个process_view() 返回了HttpResponse对象,那么在它后面process_view将会被省略,不会被执行。

django中的中间件机制和执行顺序的更多相关文章

  1. [Django高级]理解django中的中间件机制和执行顺序

    原文来自 Understanding Django Middlewares, 这篇文章从整体上介绍了django中中间件定义,作用,和怎么样自己写中间件 –orangleliu. 注:middlewa ...

  2. Django中Middleware中间件

    Django中Middleware中间件 1 Middleware中间件概述 django中间middleware实质就是一个类,django会根据自己的规则在合适的时机执行中间件相应的方法.实际上当 ...

  3. ASP.NET Core 2.2 十八.各种Filter的内部处理机制及执行顺序

    ASP.NET core 的Filter是系统中经常用到的,本文详细分享一下各种Filter定义.执行的内部机制以及执行顺序.(ASP.NET Core 系列目录) 一. 概述 ASP.NET Cor ...

  4. Django中的中间件(middleware)

    中间件: 在研究中间件的时候我们首先要知道 1 什么是中间件? 官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于在全局范围内改变Djang ...

  5. Django 中的中间件

    Django 中的中间件 Django  中间件 中间件介绍 前戏 之前在判断登录的时候使用的是装饰器的功能,通过给视图函数加装饰器来增加判断是否登录的功能.但此方法的缺点是必须给每个需要判断登录的视 ...

  6. Django 中CSRF中间件 'django.middleware.csrf.CsrfViewMiddleware',

    1.Django中CSRF中间件的工作原理及form表单提交需要添加{% csrf_token %}防止出现403错误 CSRF # 表示django全局发送post请求均需要字符串验证功能:防止跨站 ...

  7. jquery ajax中success与complete的执行顺序

    jquery ajax中success与complete的执行顺序 jquery中各个事件执行顺序如下: 1.ajaxStart(全局事件) 2.beforeSend 3.ajaxSend(全局事件) ...

  8. 错误处理之try、catch、finally中的return、throw执行顺序。

    今天遇到一个让人无语的代码块 try { bilSheetService.syncUser(bilWebseviceLog, userId, optType); }catch (Exception e ...

  9. java中子类继承父类程序执行顺序

    java中子类继承父类程序执行顺序 FatherTest.java public class FatherTest { private String name; public FatherTest() ...

随机推荐

  1. tensorflow 导入gfile模型文件

    with tf.gfile.GFile(os.path.join(self.model_dir, 'ner_model.pb'), 'rb') as f: graph_def = self.tf.Gr ...

  2. Jenkins : 安装 master 和 slave

    目录 安装 master 安装 slave 设置 master 与 slave 的通信方式 添加 slave 配置 在 salve 上安装 jre 安装并配置 Jenkins salve Jenkin ...

  3. 偏流角(Draft Angle)在等距螺旋中的作用

    劳动改变人,思维改变世界.我们可以接着聊螺旋线了. 在飞行程序设计中,偏流角(Draft Angle简写为DA)通常指得是受侧风影响航向偏移的最大角度.用速度向量来表示时,是图1中的三角形关系: 图1 ...

  4. win10 uwp 如何开始写 uwp 程序

    本文告诉大家如何创建一个 UWP 程序. 这是一系列的 uwp 入门博客,所以写的很简单 本文来告诉大家如何创建一个简单的程序 安装 VisualStudio 在开始写 UWP 需要安装 Visual ...

  5. JAVA 的输入与输出流当中,什么时候该使用字符流?什么时候该使用字节流?

    1. InputStream 和OutputStream,两个是为字节流设计的,主要用来处理字节或二进制对象, 2. Reader和 Writer.两个是为字符流(一个字符占两个字节)设计的,主要用来 ...

  6. Effective Java通俗理解(上)

    这篇博客是Java经典书籍<Effective Java(第二版)>的读书笔记,此书共有78条关于编写高质量Java代码的建议,我会试着逐一对其进行更为通俗易懂地讲解,故此篇博客的更新大约 ...

  7. Vsftpd+Tengine+SpringMVC实现上传图片

    第三部分:SpringMVC实现上传 1.1 思路 (1)使用SpringMVC上传组件,从页面表单接收图片 (2)使用vsftpd组件,将图片上传到Linux服务器 a.服务端:在Linux上安装f ...

  8. Django Rest Framework之用户频率/访问次数限制

    内置接口代码基本结构 settings.py: REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES':['api.utils.mythrottle.UserThr ...

  9. SQL Server 中的一些概念

    学习SQL Server 2012编程入门经典(第4版)的笔记 1.事务日志 任意数据库的更改起初不进入数据库本身,而是不断地被写入到事务日志. 日志是数据进入磁盘上的最先位置. 2.表 数据库中实际 ...

  10. Python全栈学习_day009知识点

    今日大纲: . 函数的初识 . 函数的返回值 . 函数的参数 1. 函数的初识 统计字符串s的总个数(不能用len) s='fkahfkahofijalkfkadhfkjadhf' count = f ...