Переговоры по содержанию

HTTP предусматривает несколько механизмов для «согласования содержания» - процесс выбора наилучшего представления для данного ответа при наличии нескольких представлений.

‒ :doc:`RFC 2616 <https://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html>`** , Fielding et al.

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

Определение принятого рендерера

Фреймворк REST использует простой стиль согласования контента для определения того, какой тип медиа должен быть возвращен клиенту, на основе доступных рендереров, приоритетов каждого из них и заголовка Accept: клиента. Используемый стиль частично зависит от клиента, а частично от сервера.

  1. Более конкретным типам носителей отдается предпочтение перед менее конкретными типами носителей.

  2. Если несколько типов медиа имеют одинаковую специфику, то предпочтение отдается на основе упорядочивания рендереров, настроенных для данного представления.

Например, учитывая следующий заголовок Accept:

application/json; indent=4, application/json, application/yaml, text/html, */*

Приоритеты для каждого из указанных типов носителей будут следующими:

  • application/json; indent=4

  • application/json , application/yaml и text/html

  • */*

Если запрашиваемое представление было настроено только с рендерерами для YAML и HTML , то REST framework будет выбирать тот рендерер, который указан первым в списке renderer_classes или в настройках DEFAULT_RENDERER_CLASSES.

Для получения дополнительной информации о заголовке HTTP Accept смотрите RFC 2616.


Примечание : Значения «q» не учитываются REST-фреймворком при определении предпочтений. Использование значений «q» негативно влияет на кэширование, и, по мнению автора, это ненужный и слишком сложный подход к согласованию контента.

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


Согласование пользовательского контента

Маловероятно, что вы захотите предоставить пользовательскую схему согласования содержимого для REST-фреймворка, но вы можете сделать это при необходимости. Для реализации пользовательской схемы согласования содержимого переопределите BaseContentNegotiation.

Классы согласования содержимого фреймворка REST обрабатывают выбор как соответствующего парсера для запроса, так и соответствующего рендеринга для ответа, поэтому вы должны реализовать оба метода .select_parser(request, parsers) и .select_renderer(request, renderers, format_suffix).

Метод select_parser() должен вернуть один из экземпляров парсера из списка доступных парсеров, или None, если ни один из парсеров не может обработать входящий запрос.

Метод select_renderer() должен возвращать кортеж из (экземпляр рендерера, тип медиа) или вызывать исключение NotAcceptable.

Пример

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

from rest_framework.negotiation import BaseContentNegotiation

class IgnoreClientContentNegotiation(BaseContentNegotiation):
    def select_parser(self, request, parsers):
        """
        Select the first parser in the `.parser_classes` list.
        """
        return parsers[0]

    def select_renderer(self, request, renderers, format_suffix):
        """
        Select the first renderer in the `.renderer_classes` list.
        """
        return (renderers[0], renderers[0].media_type)

Настройка согласования содержания

Класс согласования содержимого по умолчанию можно установить глобально, используя параметр DEFAULT_CONTENT_NEGOTIATION_CLASS. Например, следующие настройки будут использовать наш пример класса IgnoreClientContentNegotiation.

REST_FRAMEWORK = {
    'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'myapp.negotiation.IgnoreClientContentNegotiation',
}

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

from myapp.negotiation import IgnoreClientContentNegotiation
from rest_framework.response import Response
from rest_framework.views import APIView

class NoNegotiationView(APIView):
    """
    An example view that does not perform content negotiation.
    """
    content_negotiation_class = IgnoreClientContentNegotiation

    def get(self, request, format=None):
        return Response({
            'accepted media type': request.accepted_renderer.media_type
        })
Вернуться на верх