django-rest-framework/api-guide/content-negotiation/index.html
2017-05-12 16:15:00 +00:00

544 lines
22 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Content negotiation - Django REST framework</title>
<link href="../../img/favicon.ico" rel="icon" type="image/x-icon">
<link rel="canonical" href="http://www.django-rest-framework.org/api-guide/content-negotiation/" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Django, API, REST, Content negotiation">
<meta name="author" content="Tom Christie">
<!-- Le styles -->
<link href="../../css/prettify.css" rel="stylesheet">
<link href="../../css/bootstrap.css" rel="stylesheet">
<link href="../../css/bootstrap-responsive.css" rel="stylesheet">
<link href="../../css/default.css" rel="stylesheet">
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-18852272-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#sidebarInclude img {
margin-bottom: 10px;
}
#sidebarInclude a.promo {
color: black;
}
@media (max-width: 767px) {
div.promo {
display: none;
}
}
</style>
</head>
<body onload="prettyPrint()" class="-page">
<div class="wrapper">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="repo-link btn btn-primary btn-small" href="https://github.com/encode/django-rest-framework/tree/master">GitHub</a>
<a class="repo-link btn btn-inverse btn-small " rel="prev" href="../metadata/">
Next <i class="icon-arrow-right icon-white"></i>
</a>
<a class="repo-link btn btn-inverse btn-small " rel="next" href="../versioning/">
<i class="icon-arrow-left icon-white"></i> Previous
</a>
<a id="search_modal_show" class="repo-link btn btn-inverse btn-small" href="#mkdocs_search_modal" data-toggle="modal" data-target="#mkdocs_search_modal"><i class="icon-search icon-white"></i> Search</a>
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="http://www.django-rest-framework.org">Django REST framework</a>
<div class="nav-collapse collapse">
<!-- Main navigation -->
<ul class="nav navbar-nav">
<li >
<a href="../..">Home</a>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Tutorial <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../../tutorial/quickstart/">Quickstart</a>
</li>
<li >
<a href="../../tutorial/1-serialization/">1 - Serialization</a>
</li>
<li >
<a href="../../tutorial/2-requests-and-responses/">2 - Requests and responses</a>
</li>
<li >
<a href="../../tutorial/3-class-based-views/">3 - Class based views</a>
</li>
<li >
<a href="../../tutorial/4-authentication-and-permissions/">4 - Authentication and permissions</a>
</li>
<li >
<a href="../../tutorial/5-relationships-and-hyperlinked-apis/">5 - Relationships and hyperlinked APIs</a>
</li>
<li >
<a href="../../tutorial/6-viewsets-and-routers/">6 - Viewsets and routers</a>
</li>
<li >
<a href="../../tutorial/7-schemas-and-client-libraries/">7 - Schemas and client libraries</a>
</li>
</ul>
</li>
<li class="dropdown active">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Guide <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../requests/">Requests</a>
</li>
<li >
<a href="../responses/">Responses</a>
</li>
<li >
<a href="../views/">Views</a>
</li>
<li >
<a href="../generic-views/">Generic views</a>
</li>
<li >
<a href="../viewsets/">Viewsets</a>
</li>
<li >
<a href="../routers/">Routers</a>
</li>
<li >
<a href="../parsers/">Parsers</a>
</li>
<li >
<a href="../renderers/">Renderers</a>
</li>
<li >
<a href="../serializers/">Serializers</a>
</li>
<li >
<a href="../fields/">Serializer fields</a>
</li>
<li >
<a href="../relations/">Serializer relations</a>
</li>
<li >
<a href="../validators/">Validators</a>
</li>
<li >
<a href="../authentication/">Authentication</a>
</li>
<li >
<a href="../permissions/">Permissions</a>
</li>
<li >
<a href="../throttling/">Throttling</a>
</li>
<li >
<a href="../filtering/">Filtering</a>
</li>
<li >
<a href="../pagination/">Pagination</a>
</li>
<li >
<a href="../versioning/">Versioning</a>
</li>
<li class="active" >
<a href="./">Content negotiation</a>
</li>
<li >
<a href="../metadata/">Metadata</a>
</li>
<li >
<a href="../schemas/">Schemas</a>
</li>
<li >
<a href="../format-suffixes/">Format suffixes</a>
</li>
<li >
<a href="../reverse/">Returning URLs</a>
</li>
<li >
<a href="../exceptions/">Exceptions</a>
</li>
<li >
<a href="../status-codes/">Status codes</a>
</li>
<li >
<a href="../testing/">Testing</a>
</li>
<li >
<a href="../settings/">Settings</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Topics <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../../topics/documenting-your-api/">Documenting your API</a>
</li>
<li >
<a href="../../topics/api-clients/">API Clients</a>
</li>
<li >
<a href="../../topics/internationalization/">Internationalization</a>
</li>
<li >
<a href="../../topics/ajax-csrf-cors/">AJAX, CSRF & CORS</a>
</li>
<li >
<a href="../../topics/html-and-forms/">HTML & Forms</a>
</li>
<li >
<a href="../../topics/browser-enhancements/">Browser Enhancements</a>
</li>
<li >
<a href="../../topics/browsable-api/">The Browsable API</a>
</li>
<li >
<a href="../../topics/rest-hypermedia-hateoas/">REST, Hypermedia & HATEOAS</a>
</li>
<li >
<a href="../../topics/third-party-packages/">Third Party Packages</a>
</li>
<li >
<a href="../../topics/tutorials-and-resources/">Tutorials and Resources</a>
</li>
<li >
<a href="../../topics/contributing/">Contributing to REST framework</a>
</li>
<li >
<a href="../../topics/project-management/">Project management</a>
</li>
<li >
<a href="../../topics/jobs/">Jobs</a>
</li>
<li >
<a href="../../topics/3.0-announcement/">3.0 Announcement</a>
</li>
<li >
<a href="../../topics/3.1-announcement/">3.1 Announcement</a>
</li>
<li >
<a href="../../topics/3.2-announcement/">3.2 Announcement</a>
</li>
<li >
<a href="../../topics/3.3-announcement/">3.3 Announcement</a>
</li>
<li >
<a href="../../topics/3.4-announcement/">3.4 Announcement</a>
</li>
<li >
<a href="../../topics/3.5-announcement/">3.5 Announcement</a>
</li>
<li >
<a href="../../topics/3.6-announcement/">3.6 Announcement</a>
</li>
<li >
<a href="../../topics/kickstarter-announcement/">Kickstarter Announcement</a>
</li>
<li >
<a href="../../topics/mozilla-grant/">Mozilla Grant</a>
</li>
<li >
<a href="../../topics/funding/">Funding</a>
</li>
<li >
<a href="../../topics/release-notes/">Release Notes</a>
</li>
</ul>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</div>
</div>
<div class="body-content">
<div class="container-fluid">
<!-- Search Modal -->
<div id="mkdocs_search_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3 id="myModalLabel">Documentation search</h3>
</div>
<div class="modal-body">
<form role="form" autocomplete="off">
<div class="form-group">
<input type="text" name="q" class="form-control" placeholder="Search..." id="mkdocs-search-query">
</div>
</form>
<div id="mkdocs-search-results"></div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<div id="table-of-contents">
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
<li class="main">
<a href="#content-negotiation">Content negotiation</a>
</li>
<li>
<a href="#determining-the-accepted-renderer">Determining the accepted renderer</a>
</li>
<li class="main">
<a href="#custom-content-negotiation">Custom content negotiation</a>
</li>
<li>
<a href="#example">Example</a>
</li>
<li>
<a href="#setting-the-content-negotiation">Setting the content negotiation</a>
</li>
<div class="promo">
<hr/>
<div id="sidebarInclude">
</div>
</ul>
</div>
</div>
<div id="main-content" class="span9">
<a class="github" href="https://github.com/encode/django-rest-framework/tree/master/rest_framework/negotiation.py">
<span class="label label-info">negotiation.py</span>
</a>
<h1 id="content-negotiation"><a class="toclink" href="#content-negotiation">Content negotiation</a></h1>
<blockquote>
<p>HTTP has provisions for several mechanisms for "content negotiation" - the process of selecting the best representation for a given response when there are multiple representations available.</p>
<p>&mdash; <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html">RFC 2616</a>, Fielding et al.</p>
</blockquote>
<p>Content negotiation is the process of selecting one of multiple possible representations to return to a client, based on client or server preferences.</p>
<h2 id="determining-the-accepted-renderer"><a class="toclink" href="#determining-the-accepted-renderer">Determining the accepted renderer</a></h2>
<p>REST framework uses a simple style of content negotiation to determine which media type should be returned to a client, based on the available renderers, the priorities of each of those renderers, and the client's <code>Accept:</code> header. The style used is partly client-driven, and partly server-driven.</p>
<ol>
<li>More specific media types are given preference to less specific media types.</li>
<li>If multiple media types have the same specificity, then preference is given to based on the ordering of the renderers configured for the given view.</li>
</ol>
<p>For example, given the following <code>Accept</code> header:</p>
<pre><code>application/json; indent=4, application/json, application/yaml, text/html, */*
</code></pre>
<p>The priorities for each of the given media types would be:</p>
<ul>
<li><code>application/json; indent=4</code></li>
<li><code>application/json</code>, <code>application/yaml</code> and <code>text/html</code></li>
<li><code>*/*</code></li>
</ul>
<p>If the requested view was only configured with renderers for <code>YAML</code> and <code>HTML</code>, then REST framework would select whichever renderer was listed first in the <code>renderer_classes</code> list or <code>DEFAULT_RENDERER_CLASSES</code> setting.</p>
<p>For more information on the <code>HTTP Accept</code> header, see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC 2616</a></p>
<hr />
<p><strong>Note</strong>: "q" values are not taken into account by REST framework when determining preference. The use of "q" values negatively impacts caching, and in the author's opinion they are an unnecessary and overcomplicated approach to content negotiation.</p>
<p>This is a valid approach as the HTTP spec deliberately underspecifies how a server should weight server-based preferences against client-based preferences.</p>
<hr />
<h1 id="custom-content-negotiation"><a class="toclink" href="#custom-content-negotiation">Custom content negotiation</a></h1>
<p>It's unlikely that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed. To implement a custom content negotiation scheme override <code>BaseContentNegotiation</code>.</p>
<p>REST framework's content negotiation classes handle selection of both the appropriate parser for the request, and the appropriate renderer for the response, so you should implement both the <code>.select_parser(request, parsers)</code> and <code>.select_renderer(request, renderers, format_suffix)</code> methods.</p>
<p>The <code>select_parser()</code> method should return one of the parser instances from the list of available parsers, or <code>None</code> if none of the parsers can handle the incoming request.</p>
<p>The <code>select_renderer()</code> method should return a two-tuple of (renderer instance, media type), or raise a <code>NotAcceptable</code> exception.</p>
<h2 id="example"><a class="toclink" href="#example">Example</a></h2>
<p>The following is a custom content negotiation class which ignores the client
request when selecting the appropriate parser or renderer.</p>
<pre><code>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)
</code></pre>
<h2 id="setting-the-content-negotiation"><a class="toclink" href="#setting-the-content-negotiation">Setting the content negotiation</a></h2>
<p>The default content negotiation class may be set globally, using the <code>DEFAULT_CONTENT_NEGOTIATION_CLASS</code> setting. For example, the following settings would use our example <code>IgnoreClientContentNegotiation</code> class.</p>
<pre><code>REST_FRAMEWORK = {
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'myapp.negotiation.IgnoreClientContentNegotiation',
}
</code></pre>
<p>You can also set the content negotiation used for an individual view, or viewset, using the <code>APIView</code> class-based views.</p>
<pre><code>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
})
</code></pre>
</div> <!--/span-->
</div> <!--/row-->
</div> <!--/.fluid-container-->
</div> <!--/.body content-->
<div id="push"></div>
</div> <!--/.wrapper -->
<footer class="span12">
<p>Documentation built with <a href="http://www.mkdocs.org/">MkDocs</a>.
</p>
</footer>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="../../js/jquery-1.8.1-min.js"></script>
<script src="../../js/prettify-1.0.js"></script>
<script src="../../js/bootstrap-2.1.1-min.js"></script>
<script src="https://fund.django-rest-framework.org/sidebar_include.js"></script>
<script>var base_url = '../..';</script>
<script src="../../mkdocs/js/require.js"></script>
<script src="../../js/theme.js"></script>
<script>
var shiftWindow = function() {
scrollBy(0, -50)
};
if (location.hash) shiftWindow();
window.addEventListener("hashchange", shiftWindow);
$('.dropdown-menu').on('click touchstart', function(event) {
event.stopPropagation();
});
// Dynamically force sidenav/dropdown to no higher than browser window
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
$(function() {
$(window).resize(function() {
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
});
});
</script>
</body>
</html>