Deal with parser encodings properly

This commit is contained in:
Tom Christie 2013-02-04 21:16:34 +00:00
parent b052c92ac3
commit 0a38bc9db8
2 changed files with 20 additions and 5 deletions

View File

@ -5,6 +5,7 @@ They give us a generic way of being able to handle various media types
on the request, such as form content or json encoded data. on the request, such as form content or json encoded data.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings
from django.http import QueryDict from django.http import QueryDict
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
from django.http.multipartparser import MultiPartParserError from django.http.multipartparser import MultiPartParserError
@ -55,8 +56,11 @@ class JSONParser(BaseParser):
`data` will be an object which is the parsed content of the response. `data` will be an object which is the parsed content of the response.
`files` will always be `None`. `files` will always be `None`.
""" """
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
try: try:
data = stream.read().decode('iso-8859-1') data = stream.read().decode(encoding)
return json.loads(data) return json.loads(data)
except ValueError as exc: except ValueError as exc:
raise ParseError('JSON parse error - %s' % six.text_type(exc)) raise ParseError('JSON parse error - %s' % six.text_type(exc))
@ -76,8 +80,11 @@ class YAMLParser(BaseParser):
`data` will be an object which is the parsed content of the response. `data` will be an object which is the parsed content of the response.
`files` will always be `None`. `files` will always be `None`.
""" """
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
try: try:
data = stream.read().decode('iso-8859-1') data = stream.read().decode(encoding)
return yaml.safe_load(data) return yaml.safe_load(data)
except (ValueError, yaml.parser.ParserError) as exc: except (ValueError, yaml.parser.ParserError) as exc:
raise ParseError('YAML parse error - %s' % six.u(exc)) raise ParseError('YAML parse error - %s' % six.u(exc))
@ -97,7 +104,9 @@ class FormParser(BaseParser):
`data` will be a :class:`QueryDict` containing all the form parameters. `data` will be a :class:`QueryDict` containing all the form parameters.
`files` will always be :const:`None`. `files` will always be :const:`None`.
""" """
data = QueryDict(stream.read()) parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
data = QueryDict(stream.read(), encoding=encoding)
return data return data
@ -117,11 +126,12 @@ class MultiPartParser(BaseParser):
""" """
parser_context = parser_context or {} parser_context = parser_context or {}
request = parser_context['request'] request = parser_context['request']
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
meta = request.META meta = request.META
upload_handlers = request.upload_handlers upload_handlers = request.upload_handlers
try: try:
parser = DjangoMultiPartParser(meta, stream, upload_handlers) parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding)
data, files = parser.parse() data, files = parser.parse()
return DataAndFiles(data, files) return DataAndFiles(data, files)
except MultiPartParserError as exc: except MultiPartParserError as exc:
@ -136,8 +146,11 @@ class XMLParser(BaseParser):
media_type = 'application/xml' media_type = 'application/xml'
def parse(self, stream, media_type=None, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
parser = ET.XMLParser(encoding=encoding)
try: try:
tree = ET.parse(stream) tree = ET.parse(stream, parser=parser)
except (ExpatError, ETParseError, ValueError) as exc: except (ExpatError, ETParseError, ValueError) as exc:
raise ParseError('XML parse error - %s' % six.u(exc)) raise ParseError('XML parse error - %s' % six.u(exc))
data = self._xml_convert(tree.getroot()) data = self._xml_convert(tree.getroot())

View File

@ -10,6 +10,7 @@ The wrapped request then offers a richer API, in particular :
- form overloading of HTTP method, content type and content - form overloading of HTTP method, content type and content
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings
from django.http.multipartparser import parse_header from django.http.multipartparser import parse_header
from rest_framework import HTTP_HEADER_ENCODING from rest_framework import HTTP_HEADER_ENCODING
from rest_framework import exceptions from rest_framework import exceptions
@ -92,6 +93,7 @@ class Request(object):
if self.parser_context is None: if self.parser_context is None:
self.parser_context = {} self.parser_context = {}
self.parser_context['request'] = self self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET
def _default_negotiator(self): def _default_negotiator(self):
return api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS() return api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS()