语境:
我正在开发一个带有遗留内容的 Django 1.10/Python2.7 应用程序,并且我正在准备其中的一部分以在不久的将来偿还一些技术债务。/api
为此,如果请求的 URL 是要访问 API 新应用程序(路由),我需要在应用程序中使用的中间件链上放置一些逻辑,以绕过 Django 层下的一整套层。
我的想法是在 Django 中间件和项目的自定义中间件之间引入一个新的中间件(以下注释为“自定义中间件”,作为项目所具有的示例 - 总共大约 8 个中间件,其中一些进行了数十次调用到数据库,我还不知道删除它们或将它们变成需要它们的请求/视图的装饰器的含义)。
如果urlMIDDLEWARE
以/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
,PreExisting2Middleware
对于以我开头的 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
然后我将它用作中间件的基类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
)
(...)
来自文档的相关信息:见这里
中间件顺序和分层
在请求阶段,在调用视图之前,Django 按照它在 MIDDLEWARE 中定义的顺序自上而下地应用中间件。
你可以把它想象成一个洋葱:每个中间件类都是一个包裹视图的“层”,它位于洋葱的核心。如果请求通过洋葱的所有层(每层调用get_response将请求传递到下一层),一直到核心的视图,然后响应将通过每一层(以相反的顺序)在回来的路上。
如果其中一个层决定短路并返回响应而不调用其 get_response,则该层内的洋葱层(包括视图)都不会看到请求或响应。响应只会通过请求传入的相同层返回。