Django REST framework API 指南(19):內容協商

wcode發表於2018-03-21

官方原文連結
本系列文章 github 地址
轉載請註明出處

內容協商

內容協商是基於客戶端或伺服器偏好選擇多種可能的表示之一以返回客戶端的過程。

確定接受的渲染器

REST framework 根據可用的渲染器,每個渲染器的優先順序以及客戶端的 Accept: header,使用簡單的內容協商風格來確定應將哪些媒體型別返回給客戶端。所使用的風格部分由客戶端驅動,部分由伺服器驅動。

  1. 更具體的媒體型別優先於較不特定的媒體型別。
  2. 如果多種媒體型別具有相同的特性,則優先根據為給定檢視配置的渲染器排序。

例如,給出以下 Accept header:

application/json; indent=4, application/json, application/yaml, text/html, */*
複製程式碼

每種給定媒體型別的優先順序為:

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

如果所請求的檢視僅用 YAMLHTML 的渲染器配置,則 REST framework 將選擇 renderer_classes 列表或 DEFAULT_RENDERER_CLASSES 設定中首先列出的渲染器。


注意: 確定偏好時,REST framework 不會考慮 "q" 值。使用 "q" 值會對快取產生負面影響,作者認為這是對內容協商的一種不必要和過於複雜的方法。


自定義內容協商

你不太可能希望為 REST framework 提供自定義內容協商方案,但如果需要,你可以這樣做。要實現自定義內容協商方案,請覆蓋 BaseContentNegotiation

REST framework 的內容協商類處理選擇適當的請求解析器和適當的響應渲染器,因此你應該實現 .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 setting 全域性設定。例如,以下設定將使用我們的示例 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
        })
複製程式碼

相關文章