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

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. 从零开始学 Web 之 CSS3(六)动画animation,Web字体

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  2. 完整的一次 HTTP 请求响应过程(二)

    上篇文章 我们完整的描述了计算机五层模型中的『应用层』和『运输层』,阐述了较为复杂的 TCP 协议的相关原理,相信大家一定也有所收获,那么本篇将继续五层模型的学习. 网络层 『网络层』其实解决的就是一 ...

  3. windows下mongodb基础玩法系列二CURD附加一

    windows下mongodb基础玩法系列 windows下mongodb基础玩法系列一介绍与安装 windows下mongodb基础玩法系列二CURD操作(创建.更新.读取和删除) windows下 ...

  4. Java设计模式学习记录-命令模式

    前言 这次要介绍的是命令模式,这也是一种行为型模式.最近反正没有面试机会我就写博客呗,该投的简历都投了.然后就继续看书,其实看书也会给自己带来成就感,原来以前不明白的东西,书上已经给彻底的介绍清楚了, ...

  5. 3DLut表实现log视频的后期调色原理

    现在越来越多的视频或者图像拍摄设备支持log模式,比如大疆无人机的D-Log模式等等,log模式的起源和发展就不多做介绍,其在普通显示器上显示画面通常看起来是平坦的灰色,因此也常被称为log灰视频. ...

  6. [AGC 018 E] Sightseeing plan

    STO ZKY ORZ Description 给定一张网格图和三个矩形,每次只能向上或向右走.你需要从矩形 \(A\) 中的一个点 \(S\) 出发,到达矩形 \(B\) 中的一个点 \(P\) , ...

  7. C#实现微信AES-128-CBC加密数据的解密

    小程序登录时,获得用户的信息,只是昵称,无法用作ID.而有用的数据,都加密着,腾讯给出了解密的方法: 加密数据解密算法 接口如果涉及敏感数据(如wx.getUserInfo当中的 openId 和un ...

  8. 【常用配置】Hadoop-2.6.5在Ubuntu14.04下的伪分布式配置

    core-site.xml <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet t ...

  9. 《移山之道:VSTS软件开发指南》读书笔记

    这两天看了<移山之道:VSTS软件开发指南>,对团队软件开发又有了新的认识.也许对于我们这些软件开发的新手来说,最重要的是具体技术与应用框架,但读了这本书后我感觉到,实际团队项目中工具的使 ...

  10. 从零开始学习html(二)认识标签(第一部分)——上

    一.语义化,让你的网页更好的被搜索引擎理 学习html标签过程中,主要注意两个方面的学习:标签的用途.标签在浏览器中的默认样式. 标签的用途:我们学习网页制作时,常常会听到一个词,语义化. 那么什么叫 ...