前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >了解Django中间件

了解Django中间件

作者头像
会呼吸的Coder
发布2020-02-17 17:55:18
6950
发布2020-02-17 17:55:18
举报
文章被收录于专栏:会呼吸的Coder会呼吸的Coder

什么是中间件

中间件是用于修改Django 请求或响应对象的钩子的。放置来自Django docs的中间件 的定义。

代码语言:javascript
复制
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.

何时使用中间件

如果要修改请求(即发送到视图的HttpRequest对象),则可以使用中间件。或者,您可能想要修改从视图返回的HttpResponse对象。这些都可以通过使用中间件来实现。

您可能要在视图执行之前执行操作。在这种情况下,您将使用中间件。

Django提供了一些默认的中间件。例如:AuthenticationMiddleware

很多时候,您会在视图内部使用request.user。Django希望在执行任何视图之前先设置用户属性request。Django采用中间件方法来完成此任务。因此Django提供了AuthenticationMiddleware,可以修改请求对象。

然后Django修改请求对象,如下所示:

代码语言:javascript
复制
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 setting to insert "
            "'django.contrib.sessions.middleware.SessionMiddleware' before "
            "'django.contrib.auth.middleware.AuthenticationMiddleware'."
        )
        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

同样,您可能有一个可以与不同时区的用户一起使用的应用程序。您想在向用户显示任何页面时使用用户的时区。您想在所有视图中访问用户的时区。在这种情况下,在会话中添加它是有益的。因此,您可以添加这样的中间件:

代码语言:javascript
复制
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中。因此,我们编写的TimezoneMiddleware必须在Django的在元组设置.MIDDLEWARE_CLASSES中提供AuthenticationMiddleware之后进行。

在接下来的示例中,我们将对中间件的顺序有更多的了解。

#使用中间件时要记住的事情

  • 中间件的顺序很重要。
  • 中间件只需要从类 object扩展即可。
  • 中间件可以自由实现某些方法,而不能实现其他方法。
  • 中间件可以实现process_request,但不能实现process_response和process_view。实际上,这是非常普遍的,许多的Django提供的中间件都可以做到这一点。
  • 中间件可以实现process_response,但不能实现process_request。

AuthenticationMiddleware仅实现process_request,不实现process_response。你可以在这里检查

GZipMiddleware仅实现process_response,而不实现process_request或process_view。你可以在这里看到

#编写一些中间件

确保您有一个带有URL和视图的Django的项目,并且能够访问该视图。由于我们将尝试使用进行多种操作request.user,因此请确保为您正确设置了身份验证,并request.user在此视图中显示正确的内容。

在任何应用程序中创建一个文件middleware.py。

我有一个所谓的应用books,所以我正在读书/ middleware.py中编写了此应用

代码语言:javascript
复制
class BookMiddleware(object):
	def process_request(self, request):
		print "Middleware executed"

在MIDDLEWARE_CLASSES中添加此中间件

代码语言:javascript
复制
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',
)
代码语言:javascript
复制
向任何网址提出请求。这应该在runserver控制台上打印
代码语言:javascript
复制
Middleware executed

修改BookMiddleware.process_request更改看起来像

代码语言:javascript
复制
class BookMiddleware(object):
	def process_request(self, request):
		print "Middleware executed"
		print request.user

再次请求一个URL。这将引发错误。

代码语言:javascript
复制
'WSGIRequest' object has no attribute 'user'

发生这种情况是因为user尚未设置属性request

现在更改中间件的顺序,刹车BookMiddleware在AuthenticationMiddleware之后

代码语言:javascript
复制
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',
)

向任何网址提出请求。这应该在runserver控制台上打印

代码语言:javascript
复制
Middleware executed
<username>

这表明process_request已在中间件上按设置中列出的顺序执行了该命令.MIDDLEWARE_CLASSES

您可以进一步验证。在您的middleware.py中添加另一个中间件类AnotherMiddleware(对象):

代码语言:javascript
复制
	def process_request(self, request):
		print "Another middleware executed"

也可以将中间件添加到MIDDLEWARE_CLASSES中。

代码语言:javascript
复制
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',
)

现在输出为:

代码语言:javascript
复制
Middleware executed
<username>
Another middleware executed
如何从process_request返回HttpResponse改变了事情

修改BookMiddleware,可以看起来像

代码语言:javascript
复制
class BookMiddleware(object):
	def process_request(self, request):
		print "Middleware executed"
		print request.user
		return HttpResponse("some response")

现在尝试任何网址,您的输出将是:

代码语言:javascript
复制
Middleware executed
<username>

您会注意到两件事:

  • 您的视图将不再执行,并且无论您尝试使用哪个网址,都将看到“一些响应”。
  • AnotherMiddleware.process_request将不再执行。

因此,如果中间件的process_request()返回HttpResponse对象,则绕过任何后续中间件的process_request。同时查看执行被绕过。

注释“ return HttpResponse(“一些响应”)”,因此两个中间件的process_request继续执行。

#使用process_response

将方法process_response添加到两个中间件

代码语言:javascript
复制
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

尝试一些网址。输出将是

代码语言:javascript
复制
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按照在MIDDLEWARE_CLASSES中自上而下定义的顺序应用中间件的process_view()。这是process_request()遵循的顺序。

同样,如果任何process_view()返回HttpResponse对象,则随后的process_view()调用将被忽略和不执行。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 初级程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是中间件
  • 何时使用中间件
  • #使用中间件时要记住的事情
  • #编写一些中间件
    • 如何从process_request返回HttpResponse改变了事情
      • #使用process_response
        • # process_view
        相关产品与服务
        消息队列 TDMQ
        消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档