mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-04 12:23:17 +03:00
Add 'STRICT_JSON' API setting.
STRICT_JSON controls the renderer & parser behavior on whether or not to accept non-standard float values (NaN, Infinity).
This commit is contained in:
parent
d740bae95a
commit
8ab75a2f01
|
@ -22,6 +22,7 @@ from django.utils.six.moves.urllib import parse as urlparse
|
||||||
|
|
||||||
from rest_framework import renderers
|
from rest_framework import renderers
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.utils import json
|
from rest_framework.utils import json
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ class JSONParser(BaseParser):
|
||||||
"""
|
"""
|
||||||
media_type = 'application/json'
|
media_type = 'application/json'
|
||||||
renderer_class = renderers.JSONRenderer
|
renderer_class = renderers.JSONRenderer
|
||||||
|
strict = api_settings.STRICT_JSON
|
||||||
|
|
||||||
def parse(self, stream, media_type=None, parser_context=None):
|
def parse(self, stream, media_type=None, parser_context=None):
|
||||||
"""
|
"""
|
||||||
|
@ -63,7 +65,8 @@ class JSONParser(BaseParser):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
decoded_stream = codecs.getreader(encoding)(stream)
|
decoded_stream = codecs.getreader(encoding)(stream)
|
||||||
return json.load(decoded_stream)
|
parse_constant = json.strict_constant if self.strict else None
|
||||||
|
return json.load(decoded_stream, parse_constant=parse_constant)
|
||||||
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))
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ class JSONRenderer(BaseRenderer):
|
||||||
encoder_class = encoders.JSONEncoder
|
encoder_class = encoders.JSONEncoder
|
||||||
ensure_ascii = not api_settings.UNICODE_JSON
|
ensure_ascii = not api_settings.UNICODE_JSON
|
||||||
compact = api_settings.COMPACT_JSON
|
compact = api_settings.COMPACT_JSON
|
||||||
|
strict = api_settings.STRICT_JSON
|
||||||
|
|
||||||
# We don't set a charset because JSON is a binary encoding,
|
# We don't set a charset because JSON is a binary encoding,
|
||||||
# that can be encoded as utf-8, utf-16 or utf-32.
|
# that can be encoded as utf-8, utf-16 or utf-32.
|
||||||
|
@ -101,7 +102,7 @@ class JSONRenderer(BaseRenderer):
|
||||||
ret = json.dumps(
|
ret = json.dumps(
|
||||||
data, cls=self.encoder_class,
|
data, cls=self.encoder_class,
|
||||||
indent=indent, ensure_ascii=self.ensure_ascii,
|
indent=indent, ensure_ascii=self.ensure_ascii,
|
||||||
separators=separators
|
allow_nan=not self.strict, separators=separators
|
||||||
)
|
)
|
||||||
|
|
||||||
# On python 2.x json.dumps() returns bytestrings if ensure_ascii=True,
|
# On python 2.x json.dumps() returns bytestrings if ensure_ascii=True,
|
||||||
|
|
|
@ -110,6 +110,7 @@ DEFAULTS = {
|
||||||
# Encoding
|
# Encoding
|
||||||
'UNICODE_JSON': True,
|
'UNICODE_JSON': True,
|
||||||
'COMPACT_JSON': True,
|
'COMPACT_JSON': True,
|
||||||
|
'STRICT_JSON': True,
|
||||||
'COERCE_DECIMAL_TO_STRING': True,
|
'COERCE_DECIMAL_TO_STRING': True,
|
||||||
'UPLOADED_FILES_USE_URL': True,
|
'UPLOADED_FILES_USE_URL': True,
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.files.uploadhandler import (
|
from django.core.files.uploadhandler import (
|
||||||
|
@ -9,7 +10,7 @@ from django.core.files.uploadhandler import (
|
||||||
)
|
)
|
||||||
from django.http.request import RawPostDataException
|
from django.http.request import RawPostDataException
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.six.moves import StringIO
|
from django.utils.six import BytesIO, StringIO
|
||||||
|
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from rest_framework.parsers import (
|
from rest_framework.parsers import (
|
||||||
|
@ -42,7 +43,6 @@ class TestFileUploadParser(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
class MockRequest(object):
|
class MockRequest(object):
|
||||||
pass
|
pass
|
||||||
from io import BytesIO
|
|
||||||
self.stream = BytesIO(
|
self.stream = BytesIO(
|
||||||
"Test text file".encode('utf-8')
|
"Test text file".encode('utf-8')
|
||||||
)
|
)
|
||||||
|
@ -129,6 +129,24 @@ class TestFileUploadParser(TestCase):
|
||||||
self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition
|
self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition
|
||||||
|
|
||||||
|
|
||||||
|
class TestJSONParser(TestCase):
|
||||||
|
def bytes(self, value):
|
||||||
|
return BytesIO(value.encode('utf-8'))
|
||||||
|
|
||||||
|
def test_float_strictness(self):
|
||||||
|
parser = JSONParser()
|
||||||
|
|
||||||
|
# Default to strict
|
||||||
|
for value in ['Infinity', '-Infinity', 'NaN']:
|
||||||
|
with pytest.raises(ParseError):
|
||||||
|
parser.parse(self.bytes(value))
|
||||||
|
|
||||||
|
parser.strict = False
|
||||||
|
assert parser.parse(self.bytes('Infinity')) == float('inf')
|
||||||
|
assert parser.parse(self.bytes('-Infinity')) == float('-inf')
|
||||||
|
assert math.isnan(parser.parse(self.bytes('NaN')))
|
||||||
|
|
||||||
|
|
||||||
class TestPOSTAccessed(TestCase):
|
class TestPOSTAccessed(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.factory = APIRequestFactory()
|
self.factory = APIRequestFactory()
|
||||||
|
|
|
@ -358,6 +358,19 @@ class JSONRendererTests(TestCase):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
JSONRenderer().render(x)
|
JSONRenderer().render(x)
|
||||||
|
|
||||||
|
def test_float_strictness(self):
|
||||||
|
renderer = JSONRenderer()
|
||||||
|
|
||||||
|
# Default to strict
|
||||||
|
for value in [float('inf'), float('-inf'), float('nan')]:
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
renderer.render(value)
|
||||||
|
|
||||||
|
renderer.strict = False
|
||||||
|
assert renderer.render(float('inf')) == b'Infinity'
|
||||||
|
assert renderer.render(float('-inf')) == b'-Infinity'
|
||||||
|
assert renderer.render(float('nan')) == b'NaN'
|
||||||
|
|
||||||
def test_without_content_type_args(self):
|
def test_without_content_type_args(self):
|
||||||
"""
|
"""
|
||||||
Test basic JSON rendering.
|
Test basic JSON rendering.
|
||||||
|
|
|
@ -198,3 +198,10 @@ class JsonFloatTests(TestCase):
|
||||||
|
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
json.loads("NaN")
|
json.loads("NaN")
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(STRICT_JSON=False)
|
||||||
|
class NonStrictJsonFloatTests(JsonFloatTests):
|
||||||
|
"""
|
||||||
|
'STRICT_JSON = False' should not somehow affect internal json behavior
|
||||||
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user