上下文:
我正在使用旧版东西开发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
然后,我将其用作中间件PreExisting1Middleware
和PreExisting2Middleware
的基类,这样,如果上述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,洋葱的任何层都不会 在该层(包括视图)内将看到请求或 响应。响应将仅通过以下相同的层返回 该请求通过了。