Обработка условного просмотра

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

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

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

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

Декоратор 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 для различия между установкой заголовка проверки в ответах на запросы, отправленные пользователем,PUT по сравнению сPOST .

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

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

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

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

Copyright ©2020 All rights reserved