Documentation for parsers

This commit is contained in:
Tom Christie 2012-10-14 22:43:07 +01:00
parent 7a89d7a770
commit 551c86c43a
5 changed files with 183 additions and 37 deletions

View File

@ -8,17 +8,144 @@ sending more complex data than simple forms
>
> — Malcom Tredinnick, [Django developers group][cite]
REST framework includes a number of built in Parser classes, that allow you to accept requests with various media types. There is also support for defining your own custom parsers, which gives you the flexiblity to design the media types that your API accepts.
## How the parser is determined
The set of valid parsers for a view is always defined as a list of classes. When either `request.DATA` or `request.FILES` is accessed, REST framework will examine the `Content-Type` header on the incoming request, and determine which parser to use to parse the request content.
## Setting the parsers
The default set of parsers may be set globally, using the `DEFAULT_PARSERS` setting. For example, the following settings would allow requests with `YAML` content.
REST_FRAMEWORK = {
'DEFAULT_PARSERS': (
'rest_framework.parsers.YAMLParser',
)
}
You can also set the renderers used for an individual view, using the `APIView` class based views.
class ExampleView(APIView):
"""
A view that can accept POST requests with YAML content.
"""
parser_classes = (YAMLParser,)
def post(self, request, format=None):
return Response({'received data': request.DATA})
Or, if you're using the `@api_view` decorator with function based views.
@api_view(('POST',)),
@parser_classes((YAMLParser,))
def example_view(request, format=None):
"""
A view that can accept POST requests with YAML content.
"""
return Response({'received data': request.DATA})
---
# API Reference
## JSONParser
Parses `JSON` request content.
**.media_type**: `application/json`
## YAMLParser
Parses `YAML` request content.
**.media_type**: `application/yaml`
## XMLParser
Parses REST framework's default style of `XML` request content.
Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`.
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
**.media_type**: `application/xml`
## FormParser
Parses HTML form content. `request.DATA` will be populated with a `QueryDict` of data, `request.FILES` will be populated with an empty `QueryDict` of data.
You will typically want to use both `FormParser` and `MultiPartParser` together in order to fully support HTML form data.
**.media_type**: `application/x-www-form-urlencoded`
## MultiPartParser
## Custom parsers
Parses multipart HTML form content, which supports file uploads. Both `request.DATA` and `request.FILES` will be populated with a `QueryDict`.
You will typically want to use both `FormParser` and `MultiPartParser` together in order to fully support HTML form data.
**.media_type**: `multipart/form-data`
---
# Custom parsers
To implement a custom parser, you should override `BaseParser`, set the `.media_type` property, and implement the `.parse_stream(self, stream, parser_context)` method.
The method should return the data that will be used to populate the `request.DATA` property.
For example:
class PlainTextParser(BaseParser):
"""
Plain text parser.
"""
media_type = 'text/plain'
def parse_stream(self, stream, parser_context=None):
"""
Simply return a string representing the body of the request.
"""
return stream.read()
The arguments passed to `.parse_stream()` are:
### stream
A stream-like object representing the body of the request.
### parser_context
If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. By default it includes the keys `'upload_handlers'` and `'meta'`, which contain the values of the `request.upload_handlers` and `request.meta` properties.
## Uploading file content
If your custom parser needs to support file uploads, you may return a `DataAndFiles` object from the `.parse_stream()` method. `DataAndFiles` should be instantiated with two arguments. The first argument will be used to populate the `request.DATA` property, and the second argument will be used to populate the `request.FILES` property.
For example:
class SimpleFileUploadParser(BaseParser):
"""
A naive raw file upload parser.
"""
def parse_stream(self, stream, parser_context):
content = stream.read()
name = 'example.dat'
content_type = 'application/octet-stream'
size = len(content)
charset = 'utf-8'
# Write a temporary file based on the request content
temp = tempfile.NamedTemporaryFile(delete=False)
temp.write(content)
uploaded = UploadedFile(temp, name, content_type, size, charset)
# Return the uploaded file
data = {}
files = {name: uploaded}
return DataAndFiles(data, files)
[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion

View File

@ -42,8 +42,8 @@ You can also set the renderers used for an individual view, using the `APIView`
Or, if you're using the `@api_view` decorator with function based views.
@api_view('GET'),
@renderer_classes(JSONRenderer, JSONPRenderer)
@api_view(('GET',)),
@renderer_classes((JSONRenderer, JSONPRenderer))
def user_count_view(request, format=None):
"""
A view that returns the count of active users, in JSON or JSONp.
@ -66,27 +66,45 @@ If your API includes views that can serve both regular webpages and API response
## JSONRenderer
**.media_type:** `application/json`
Renders the request data into `JSON`.
**.format:** `'.json'`
The client may additionally include an `'indent'` media type parameter, in which case the returned `JSON` will be indented. For example `Accept: application/json; indent=4`.
**.media_type**: `application/json`
**.format**: `'.json'`
## JSONPRenderer
**.media_type:** `application/javascript`
Renders the request data into `JSONP`. The `JSONP` media type provides a mechanism of allowing cross-domain AJAX requests, by wrapping a `JSON` response in a javascript callback.
**.format:** `'.jsonp'`
The javascript callback function must be set by the client including a `callback` URL query parameter. For example `http://example.com/api/users?callback=jsonpCallback`. If the callback function is not explicitly set by the client it will default to `'callback'`.
**Note**: If you require cross-domain AJAX requests, you may also want to consider using [CORS] as an alternative to `JSONP`.
**.media_type**: `application/javascript`
**.format**: `'.jsonp'`
## YAMLRenderer
**.media_type:** `application/yaml`
Renders the request data into `YAML`.
**.format:** `'.yaml'`
**.media_type**: `application/yaml`
**.format**: `'.yaml'`
## XMLRenderer
**.media_type:** `application/xml`
Renders REST framework's default style of `XML` response content.
**.format:** `'.xml'`
Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`.
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
**.media_type**: `application/xml`
**.format**: `'.xml'`
## HTMLRenderer
@ -118,19 +136,21 @@ You can use `HTMLRenderer` either to return regular HTML pages using REST framew
If you're building websites that use `HTMLRenderer` along with other renderer classes, you should consider listing `HTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers.
**.media_type:** `text/html`
**.media_type**: `text/html`
**.format:** `'.html'`
**.format**: `'.html'`
## BrowsableAPIRenderer
Renders data into HTML for the Browseable API. This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page.
**.media_type:** `text/html`
**.media_type**: `text/html`
**.format:** `'.api'`
**.format**: `'.api'`
## Custom renderers
---
# Custom renderers
To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type=None, renderer_context=None)` method.
@ -151,15 +171,15 @@ For example:
The arguments passed to the `.render()` method are:
#### `data`
### `data`
The request data, as set by the `Response()` instantiation.
#### `media_type=None`
### `media_type=None`
Optional. If provided, this is the accepted media type, as determined by the content negotiation stage. Depending on the client's `Accept:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"application/json; nested=true"`.
#### `renderer_context=None`
### `renderer_context=None`
Optional. If provided, this is a dictionary of contextual information provided by the view.
By default this will include the following keys: `view`, `request`, `response`, `args`, `kwargs`.
@ -213,6 +233,7 @@ For good examples of custom media types, see GitHub's use of a custom [applicati
[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process
[conneg]: content-negotiation.md
[browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers
[CORS]: http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
[HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas
[quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[application/vnd.github+json]: http://developer.github.com/v3/media/

View File

@ -45,6 +45,8 @@ You won't typically need to access this property.
**Note:** If a client sends malformed content, then accessing `request.DATA` or `request.FILES` may raise a `ParseError`. By default REST framework's `APIView` class or `@api_view` decorator will catch the error and return a `400 Bad Request` response.
If a client sends a request with a content-type that cannot be parsed then a `UnsupportedMediaType` exception will be raised, which by default will be caught and return a `415 Unsupported Media Type` response.
---
# Authentication

View File

@ -98,23 +98,6 @@ class YAMLParser(BaseParser):
raise ParseError('YAML parse error - %s' % unicode(exc))
class PlainTextParser(BaseParser):
"""
Plain text parser.
"""
media_type = 'text/plain'
def parse_stream(self, stream, **opts):
"""
Returns a 2-tuple of `(data, files)`.
`data` will simply be a string representing the body of the request.
`files` will always be `None`.
"""
return stream.read()
class FormParser(BaseParser):
"""
Parser for form data.

View File

@ -10,9 +10,9 @@ from rest_framework import status
from rest_framework.authentication import SessionAuthentication
from django.test.client import RequestFactory
from rest_framework.parsers import (
BaseParser,
FormParser,
MultiPartParser,
PlainTextParser,
JSONParser
)
from rest_framework.request import Request
@ -24,6 +24,19 @@ from rest_framework.views import APIView
factory = RequestFactory()
class PlainTextParser(BaseParser):
media_type = 'text/plain'
def parse_stream(self, stream, **opts):
"""
Returns a 2-tuple of `(data, files)`.
`data` will simply be a string representing the body of the request.
`files` will always be `None`.
"""
return stream.read()
class TestMethodOverloading(TestCase):
def test_method(self):
"""