4

语境:

我正在开发一个带有遗留内容的 Django 1.10/Python2.7 应用程序,并且我正在准备其中的一部分以在不久的将来偿还一些技术债务。/api为此,如果请求的 URL 是要访问 API 新应用程序(路由),我需要在应用程序中使用的中间件链上放置一些逻辑,以绕过 Django 层下的一整套层。

我的想法是在 Django 中间件和项目的自定义中间件之间引入一个新的中间件(以下注释为“自定义中间件”,作为项目所具有的示例 - 总共大约 8 个中间件,其中一些进行了数十次调用到数据库,我还不知道删除它们或将它们变成需要它们的请求/视图的装饰器的含义)。

如果urlMIDDLEWARE/api.

正如他们在文档中所说,我尝试在 Django 中短路中间件链,但这对我不起作用。

我是如何工作的(但不理想):

我让它工作的方式是这样做(这不是他们在文档中所说的):

这是项目中预先存在的MIDDLEWAREsettings.py

(...)
MIDDLEWARE = (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',

    'app2.middleware.PreExisting1Middleware',                   # Custom middlewares
    'app3.middleware.PreExisting2Middleware',                   # Custom middlewares
)
(...)

我添加了新settings.py密钥:

SHORTCIRCUIT_MIDDLWARES_IF_URL_PATTERNS_IN = (r'^/api', )

为了避免应用PreExisting1MiddlewarePreExisting2Middleware对于以我开头的 URL 路由,/api我创建了一个基本的中间件类,如下所示:

import re

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.deprecation import MiddlewareMixin


class ShortCircuitMiddlewareMixin(MiddlewareMixin):
    def __call__(self, request):
        # Code to be executed for each request before the view (and later middleware) are called.
        response = None
        if hasattr(self, 'process_request') and self.should_middleware_be_applied(request):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response') and self.should_middleware_be_applied(request):
            response = self.process_response(request, response)
        return response

    def should_middleware_be_applied(self, request):
        short_patterns = getattr(settings, 'SHORTCIRCUIT_MIDDLWARES_IF_URL_PATTERNS_IN', [])
        if hasattr(short_patterns, '__iter__'):
            if any(re.match(pattern, request.path) for pattern in short_patterns):
                print('\nShort-circuiting the middleware: {}'.format(self.__class__.__name__))
                return False
        else:
            raise ImproperlyConfigured(
                "SHORTCIRCUIT_MIDDLWARES_IF_URL_PATTERNS_IN must be an iterable, got '{}'".format(short_patterns))
        return True

然后我将它用作中间件的基类PreExisting1MiddlewarePreExisting2Middleware这样,如果所述 url 条件成立,它们就会短路。

Class PreExisting1Middleware(ShortCircuitMiddlewareMixin):
    def process_request(self, request):
        print('\nprocessing...')
        (...)

Class PreExisting2Middleware(ShortCircuitMiddlewareMixin):
    def process_request(self, request):
        (...)

这工作得很好,我在 shell 上收到一条很好的消息,说明哪些中间件被绕过了,如果需要也可以/应该记录。

现在,关于这个问题......

我的问题:

__call__关于如何通过在下面的方法和下面的 MIDDLEWARE 配置中使用正确的逻辑来做到这一点的任何想法?

class ConditionallyShortcircuitChainMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response
(...)
MIDDLEWARE = (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',

    'app1.middleware.ConditionallyShortcircuitChainMiddleware', # My new middleware
    'app2.middleware.PreExisting1Middleware',                   # Custom
    'app3.middleware.PreExisting2Middleware',                   # Custom
)
(...)

来自文档的相关信息:见这里

中间件顺序和分层

在请求阶段,在调用视图之前,Django 按照它在 MIDDLEWARE 中定义的顺序自上而下地应用中间件。

你可以把它想象成一个洋葱:每个中间件类都是一个包裹视图的“层”,它位于洋葱的核心。如果请求通过洋葱的所有层(每层调用get_response将请求传递到下一层),一直到核心的视图,然后响应将通过每一层(以相反的顺序)在回来的路上。

如果其中一个层决定短路并返回响应而不调用其 get_response,则该层内的洋葱层(包括视图)都不会看到请求或响应。响应只会通过请求传入的相同层返回。

4

0 回答 0