negotiation.py

コンテンツネゴシエーション

HTTPは、「コンテンツネゴシエーション」のためのいくつかのメカニズムを提供しています。コンテンツネゴシエーションとは、複数の表現が利用可能な場合に、与えられたレスポンスに対して最適な表現を選択するプロセスです。

RFC 2616, Fielding et al.

コンテンツネゴシエーションとは、クライアントまたはサーバーのプリファレンスに基づいて、クライアントに返す複数の表現の中から1つを選択するプロセスです。

受け入れられるレンダラーの決定

REST framework は、利用可能なレンダラー、各レンダラーの優先順位、およびクライアントの Accept: ヘッダーに基づいて、クライアントに返すメディアタイプを決定するために、シンプルなスタイルのコンテンツネゴシエーションを使用します。使用されるスタイルは、部分的にクライアント主導であり、部分的にサーバー主導です。

  1. より具体的なメディアタイプは、より具体的なメディアタイプよりも優先されます。
  2. 複数のメディアタイプが同じ特異性を持つ場合、指定されたビューに設定されたレンダラーの順序に基づいて優先順位が付けられます。

例えば、以下の`Accept`ヘッダーが与えられた場合

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

各メディアタイプの優先順位は次のようになります。

  • application/json; indent=4
  • application/jsonapplication/yamltext/html
  • */*

リクエストされたビューが`YAML`と`HTML`のレンダラーのみで設定されている場合、REST framework は `renderer_classes` リストまたは `DEFAULT_RENDERER_CLASSES` 設定に最初にリストされているレンダラーを選択します。

`HTTP Accept` ヘッダーの詳細については、RFC 2616 を参照してください。


注意: REST framework は、プリファレンスを決定する際に "q" 値を考慮しません。"q" 値を使用するとキャッシングに悪影響を及ぼし、著者の意見では、コンテンツネゴシエーションに対する不要で複雑すぎるアプローチです。

これは、HTTP仕様では、サーバーがサーバーベースのプリファレンスとクライアントベースのプリファレンスをどのように重み付けすべきかを意図的に明確にしていないため、有効なアプローチです。


カスタムコンテンツネゴシエーション

REST framework のカスタムコンテンツネゴシエーションスキームを提供することはまれですが、必要に応じて行うことができます。カスタムコンテンツネゴシエーションスキームを実装するには、`BaseContentNegotiation` をオーバーライドします。

REST framework のコンテンツネゴシエーションクラスは、リクエストに適切なパーサーとレスポンスに適切なレンダラーの両方を選択するため、 `select_parser(request, parsers)` メソッドと `select_renderer(request, renderers, format_suffix)` メソッドの両方を実装する必要があります。

`select_parser()` メソッドは、利用可能なパーサーのリストからパーサーインスタンスの1つを返すか、パーサーが入力リクエストを処理できない場合は `None` を返します。

`select_renderer()` メソッドは、(レンダラーインスタンス、メディアタイプ) の2つのタプルを返すか、`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
        })