Условный View Processing

HTTP-клиенты могут отправлять несколько заголовков, чтобы сообщить серверу о копиях ресурса, которые они уже видели. Это обычно используется при получении веб-страницы (с помощью HTTP- GETзапроса), чтобы избежать отправки всех данных о том, что клиент уже получил. Тем не менее, одни и те же заголовки могут быть использованы для всех методов HTTP ( POST, PUT, DELETEи т.д.).

Для каждой страницы (ответа), которую Django отправляет обратно из представления, он может предоставить два заголовка HTTP: ETagзаголовок и Last-Modifiedзаголовок. Эти заголовки необязательны для HTTP-ответов. Они могут быть установлены вашей функцией просмотра, или вы можете полагаться на ConditionalGetMiddleware промежуточное программное обеспечение для установки ETagзаголовка.

Когда клиент в следующий раз запрашивает тот же ресурс, он может отправить заголовок, например Если-изменено-с или If-unmodified-Since , содержащий дату последней модификации, когда оно было отправлено, или либоЕсли совпадение или If-none-match , содержащий последнийETagотправленный. Если текущая версия страницы совпадает сETagотправленной клиентом, или если ресурс не был изменен, код состояния 304 может быть отправлен обратно вместо полного ответа, сообщая клиенту, что ничего не изменилось. В зависимости от заголовка, если страница была изменена или не соответствует ETagотправленной клиентом, может быть возвращен код состояния 412 (Precondition Failed).

Когда вам нужен более детальный контроль, вы можете использовать функции условной обработки для каждого представления.

conditionДекоратор

Иногда (на самом деле, довольно часто) вы можете создавать функции для быстрого вычисления значения ETag или времени последнего изменения для ресурса без необходимости выполнять все вычисления, необходимые для построения полного представления. Затем Django может использовать эти функции для предоставления возможности «раннего аварийного выхода» для обработки представления. Возможно, сообщив клиенту, что контент не изменялся с момента последнего запроса.

Эти две функции передаются django.views.decorators.http.conditionдекоратору в качестве параметров . Этот декоратор использует две функции (вам нужно указать только одну, если вы не можете легко и быстро вычислить обе величины), чтобы определить, совпадают ли заголовки в HTTP-запросе с заголовками ресурса. Если они не совпадают, необходимо вычислить новую копию ресурса и вызвать ваше обычное представление.

В conditionподписи декоратора выглядит следующим образом :

condition(etag_func=None, last_modified_func=None)

Обеим функциям для вычисления ETag и времени последнего изменения будут переданы входящий requestобъект и те же параметры в том же порядке, что и функции представления, которую они помогают обернуть. Переданная функция last_modified_funcдолжна возвращать стандартное значение даты и времени, указывающее, когда в последний раз ресурс был изменен, или Noneесли ресурс не существует. Функция, переданная etagдекоратору, должна возвращать строку, представляющую ETag для ресурса, или, Noneесли он не существует.

Декоратор устанавливает заголовки ETagи Last-Modifiedв ответе, если они еще не установлены представлением и если метод запроса безопасен ( GETили HEAD).

Использование этой функции с пользой, вероятно, лучше всего пояснить на примере. Предположим, у вас есть эта пара моделей, представляющих небольшую систему блогов:

import datetime
from django.db import models

class Blog(models.Model):
    ...

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    published = models.DateTimeField(default=datetime.datetime.now)
    ...

Если первая страница, отображающая последние записи блога, изменяется только при добавлении новой записи блога, вы можете очень быстро вычислить время последнего изменения. Вам нужна самая последняя publishedдата для каждой записи, связанной с этим блогом. Один из способов сделать это:

def latest_entry(request, blog_id):
    return Entry.objects.filter(blog=blog_id).latest("published").published

Затем вы можете использовать эту функцию, чтобы обеспечить раннее обнаружение неизмененной страницы для просмотра вашей первой страницы:

from django.views.decorators.http import condition

@condition(last_modified_func=latest_entry)
def front_page(request, blog_id):
    ...

Будьте осторожны с порядком декораторов

При condition()возврате условного ответа любые декораторы ниже него будут пропущены и не будут применяться к ответу. Следовательно, любые декораторы, которые должны применяться как к обычному ответу представления, так и к условному ответу, должны быть указаны выше condition(). В частности, vary_on_cookie(), vary_on_headers()и cache_control()должен прийти первым , потому чтоRFC 7232 требует, чтобы установленные ими заголовки присутствовали в ответах 304.

Ярлыки для вычисления только одного значения

Как правило, если вы можете предоставить функции для вычисления как ETag, так и времени последнего изменения, вы должны это сделать. Вы не знаете, какие заголовки отправит вам какой-либо конкретный HTTP-клиент, поэтому будьте готовы обработать и то, и другое. Однако иногда легко вычислить только одно значение, и Django предоставляет декораторы, которые обрабатывают только ETag или только вычисления с последними изменениями.

django.views.decorators.http.etagИ django.views.decorators.http.last_modifiedдекораторы прошли тот же тип функций , как conditionдекоратор. Их подписи:

etag(etag_func)
last_modified(last_modified_func)

Мы могли бы написать предыдущий пример, в котором используется только последняя измененная функция, используя один из этих декораторов:

@last_modified(latest_entry)
def front_page(request, blog_id):
    ...

…или же:

def front_page(request, blog_id):
    ...
front_page = last_modified(latest_entry)(front_page)

Используйте conditionпри тестировании обоих условий

Это может выглядеть лучше для некоторых людей , чтобы попытаться ЦЕПЬ etagи last_modifiedдекораторов , если вы хотите проверить , как предварительные условия. Однако это приведет к неправильному поведению.

# Bad code. Don't do this!
@etag(etag_func)
@last_modified(last_modified_func)
def my_view(request):
    # ...

# End of bad code.

Первый декоратор ничего не знает о втором и может ответить, что ответ не изменяется, даже если вторые декораторы определят иначе. conditionДекоратор использует обе функции обратного вызова одновременно выработать правильные действия предпринять.

Используя декоратор с другими методами HTTP

conditionДекоратор полезно для более чем только GETи HEADзапросов ( HEADзапросы на такие же , как GETв такой ситуации). Он также может быть использован для обеспечения проверки для POST, PUTи DELETEзапросов. В этих ситуациях идея заключается не в том, чтобы вернуть ответ «не изменен», а в том, чтобы сообщить клиенту, что ресурс, который они пытаются изменить, тем временем был изменен.

Например, рассмотрим следующий обмен между клиентом и сервером:

  1. Клиентские запросы /foo/.
  2. Сервер отвечает неким контентом с ETag "abcd1234".
  3. Клиент отправляет HTTP- PUTзапрос /foo/на обновление ресурса. Он также отправляет заголовок, чтобы указать версию, которую он пытается обновить.If-Match: "abcd1234"
  4. Сервер проверяет, изменился ли ресурс, вычисляя ETag так же, как и для GETзапроса (используя ту же функцию). Если ресурс был изменен, он будет возвращать код 412 статуса, что означает «предварительное условие не удалось».
  5. После получения ответа 412 клиент отправляет GETзапрос на /foo/получение обновленной версии контента перед ее обновлением.

Важная вещь, которую показывает этот пример, заключается в том, что одни и те же функции могут использоваться для вычисления значений ETag и последней модификации во всех ситуациях. Фактически, вы должны использовать одни и те же функции, чтобы каждый раз возвращались одни и те же значения.

Заголовки валидатора с небезопасными методами запроса

conditionДекоратор устанавливает только валидаторы заголовки ( ETagи Last-Modified) для безопасных методов HTTP, то есть GETи HEAD. Если вы хотите вернуть их в других случаях, установите их в своем представлении. Видеть RFC 7231 # section-4.3.4, чтобы узнать о различиях между установкой заголовка валидатора в ответ на запросы, сделанные с помощьюPUTversusPOST.

Сравнение с условной обработкой промежуточного ПО

Django обеспечивает условную GETобработку через django.middleware.http.ConditionalGetMiddleware. Хотя промежуточное ПО подходит для многих ситуаций, оно имеет ограничения для расширенного использования:

  • Он применяется глобально ко всем представлениям в вашем проекте.
  • Это не избавляет вас от генерации ответа, который может быть дорогостоящим.
  • Это подходит только для HTTP- GETзапросов.

Здесь вы должны выбрать наиболее подходящий инструмент для вашей конкретной проблемы. Если у вас есть способ быстро вычислить ETags и время модификации и если какое-то представление требует времени для генерации контента, вам следует рассмотреть возможность использования conditionдекоратора, описанного в этом документе. Если все уже работает довольно быстро, придерживайтесь промежуточного программного обеспечения, и объем сетевого трафика, отправляемого обратно клиентам, все равно будет уменьшен, если представление не изменилось.

Copyright ©2021 All rights reserved