Django
Промежуточное
Поиск…
Вступление
Middleware в Django - это структура, которая позволяет коду подключаться к обработке ответа / запроса и изменять ввод или вывод Django.
замечания
MIDDLEWARE_CLASSES
промежуточного уровня необходимо добавить в список settings.py MIDDLEWARE_CLASSES
прежде чем он будет включен в выполнение. Список по умолчанию, который предоставляет Django при создании нового проекта, выглядит следующим образом:
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'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',
]
Это все функции, которые будут выполняться в порядке по каждому запросу (один раз до того, как он достигнет вашего кода вида в views.py
и один раз в обратном порядке для обратного вызова process_response
, до версии 1.10). Они выполняют самые разнообразные функции, такие как инъекция маркера кросс-сайта Forgery (csrf) .
Порядок имеет значение, потому что, если какое-либо промежуточное программное обеспечение выполняет перенаправление, тогда все последующее промежуточное ПО никогда не запустится. Или, если промежуточное ПО ожидает, что токен csrf будет там, он должен запускаться после CsrfViewMiddleware
.
Добавление данных в запросы
Django упрощает добавление дополнительных данных в запросы для использования в представлении. Например, мы можем разобрать субдомен на META запроса и приложить его как отдельное свойство в запросе с помощью промежуточного программного обеспечения.
class SubdomainMiddleware:
def process_request(self, request):
"""
Parse out the subdomain from the request
"""
host = request.META.get('HTTP_HOST', '')
host_s = host.replace('www.', '').split('.')
request.subdomain = None
if len(host_s) > 2:
request.subdomain = host_s[0]
Если вы добавите данные с промежуточным программным обеспечением в свой запрос, вы сможете получить доступ к этим вновь добавленным данным дальше по строке. Здесь мы будем использовать проанализированный поддомен, чтобы определить что-то вроде того, какая организация обращается к вашему приложению. Этот подход полезен для приложений, которые развернуты с настройкой DNS с поддоменами подстановочных знаков, которые указывают на один экземпляр, и человек, обращающийся к приложению, хочет, чтобы сдержанная версия зависела от точки доступа.
class OrganizationMiddleware:
def process_request(self, request):
"""
Determine the organization based on the subdomain
"""
try:
request.org = Organization.objects.get(domain=request.subdomain)
except Organization.DoesNotExist:
request.org = None
Помните, что порядок имеет значение, когда промежуточное программное обеспечение зависит друг от друга. Для запросов вы хотите, чтобы зависимое промежуточное ПО было размещено после зависимости.
MIDDLEWARE_CLASSES = [
...
'myapp.middleware.SubdomainMiddleware',
'myapp.middleware.OrganizationMiddleware',
...
]
Среднее ПО для фильтрации по IP-адресу
Сначала: структура пути
Если у вас его нет, вам нужно создать папку промежуточного программного обеспечения в своем приложении, следуя структуре:
yourproject/yourapp/middleware
Средство промежуточного содержимого папки должно быть помещено в ту же папку, что и settings.py, urls, templates ...
Важно: Не забудьте создать пустой файл инициализации .py внутри папки промежуточного программного обеспечения, чтобы ваше приложение распознало эту папку
Вместо того, чтобы иметь отдельную папку, содержащую ваши классы промежуточного программного обеспечения, также можно разместить свои функции в одном файле - yourproject/yourapp/middleware.py
.
Второе: создание промежуточного программного обеспечения
Теперь мы должны создать файл для нашего специального промежуточного программного обеспечения. В этом примере предположим, что мы хотим, чтобы промежуточное программное обеспечение, которое фильтрует пользователей на основе их IP-адреса, мы создаем файл filter_ip_middleware.py :
#yourproject/yourapp/middleware/filter_ip_middleware.py
from django.core.exceptions import PermissionDenied
class FilterIPMiddleware(object):
# Check if client IP address is allowed
def process_request(self, request):
allowed_ips = ['192.168.1.1', '123.123.123.123', etc...] # Authorized ip's
ip = request.META.get('REMOTE_ADDR') # Get client IP address
if ip not in allowed_ips:
raise PermissionDenied # If user is not allowed raise Error
# If IP address is allowed we don't do anything
return None
Третье: добавьте промежуточное ПО в наш 'settings.py'
Нам нужно искать MIDDLEWARE_CLASSES
внутри settings.py, и там нам нужно добавить наше промежуточное ПО ( добавьте его в последнюю позицию ). Это должно выглядеть так:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Above are Django standard middlewares
# Now we add here our custom middleware
'yourapp.middleware.filter_ip_middleware.FilterIPMiddleware'
)
Готово! Теперь каждый запрос от каждого клиента вызовет ваше собственное промежуточное программное обеспечение и обработает ваш собственный код!
Исключительное исключение
Скажем, вы внедрили некоторую логику для обнаружения попыток изменения объекта в базе данных, в то время как клиент, который представил изменения, не вносил последних изменений. Если такой случай случается, вы ConfictError(detailed_message)
пользовательское исключение ConfictError(detailed_message)
.
Теперь вы хотите вернуть код статуса HTTP 409 (Confict) при возникновении этой ошибки. Обычно вы можете использовать в качестве промежуточного программного обеспечения для этого, вместо того чтобы обрабатывать его в каждом представлении, которое может вызвать это исключение.
class ConfictErrorHandlingMiddleware:
def process_exception(self, request, exception):
if not isinstance(exception, ConflictError):
return # Propagate other exceptions, we only handle ConflictError
context = dict(confict_details=str(exception))
return TemplateResponse(request, '409.html', context, status=409)
Понимание нового стиля промежуточного ПО Django 1.10
Django 1.10 представил новый стиль промежуточного process_request
котором process_request
и process_response
объединяются вместе.
В этом новом стиле промежуточное программное обеспечение является вызываемым, которое возвращает другой вызываемый . Ну, на самом деле первый является промежуточным заводом, а последний является промежуточным программным обеспечением .
Завод промежуточного ПО принимает в качестве единственного аргумента следующее промежуточное программное обеспечение в стеке middlewares или само представление, когда достигается дно стека.
Среднее ПО принимает запрос как единственный аргумент и всегда возвращает HttpResponse
.
Лучший пример для иллюстрации того, как работает промежуточное программное обеспечение нового стиля, вероятно, показывает, как создать промежуточное программное обеспечение, совместимое с обратной связью :
class MyMiddleware:
def __init__(self, next_layer=None):
"""We allow next_layer to be None because old-style middlewares
won't accept any argument.
"""
self.get_response = next_layer
def process_request(self, request):
"""Let's handle old-style request processing here, as usual."""
# Do something with request
# Probably return None
# Or return an HttpResponse in some cases
def process_response(self, request, response):
"""Let's handle old-style response processing here, as usual."""
# Do something with response, possibly using request.
return response
def __call__(self, request):
"""Handle new-style middleware here."""
response = self.process_request(request)
if response is None:
# If process_request returned None, we must call the next middleware or
# the view. Note that here, we are sure that self.get_response is not
# None because this method is executed only in new-style middlewares.
response = self.get_response(request)
response = self.process_response(request, response)
return response