如何在Django中正确短路中间件?

时间:2019-05-15 11:54:30

标签: python django api middleware technical-debt

上下文:

我正在使用旧版东西开发Django 1.10 / Python2.7应用程序,并且我正在准备其中的一部分以在不久的将来偿还一些技术债务。为此,我需要在应用程序中使用的中间件链上放置一些逻辑,以便在所请求的URL到达API新应用程序(/api路由)的情况下绕过Django下面的一整套层。

我的想法是在Django的项目和自定义的项目之间引入一个新的中间件(下面以“自定义中间件”为例,介绍了proj的例子-总共约有8个中间件,其中一些对DB的数十次调用,但我还不知道删除它们或将它们变成需要它们的请求/视图的装饰器的含义)。

如果url以MIDDLEWARE开头,则该中间件将使/api上的所有中间件短路。

我曾尝试按照Django在文档中所说的那样短路中间件链,但它对我不起作用。

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

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

这是项目MIDDLEWARE中预先存在的settings.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', )

为避免对以PreExisting1Middleware开头的URL路由应用PreExisting2Middleware/api,我创建了一个基本的Middleware类,如下所示:

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
)
(...)

文档中的相关信息: 参见here

  

中间件顺序和分层

     

在请求阶段,调用视图之前,Django将应用   中间件,按从上到下的顺序在MIDDLEWARE中定义。

     

您可以将其视为一个洋葱:每个中间件类都是一个“层”   封装了视图,这是洋葱的核心。如果要求   穿过洋葱的所有层(每一层都   get_response将请求传递到下一层),一直到   核心视图,然后响应将通过每一层   (以相反的顺序)在退出途中。

     

如果其中一层决定短路并返回响应   无需调用其get_response,洋葱的任何层都不会   在该层(包括视图)内将看到请求或   响应。响应将仅通过以下相同的层返回   该请求通过了。

0 个答案:

没有答案