diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py
index 19b287a..2c2bf71 100644
a
|
b
|
class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
|
151 | 151 | env['PATH_INFO'] = unquote(path) |
152 | 152 | env['QUERY_STRING'] = query |
153 | 153 | env['REMOTE_ADDR'] = self.client_address[0] |
154 | | env['CONTENT_TYPE'] = self.headers.get('content-type', 'text/plain') |
| 154 | env['CONTENT_TYPE'] = self.headers.get('content-type', '') |
155 | 155 | |
156 | 156 | length = self.headers.get('content-length') |
157 | 157 | if length: |
diff --git a/django/http/__init__.py b/django/http/__init__.py
index b385b45..95e5002 100644
a
|
b
|
class HttpRequest(object):
|
315 | 315 | self._post_parse_error = True |
316 | 316 | |
317 | 317 | def _load_post_and_files(self): |
318 | | # Populates self._post and self._files |
| 318 | """ Populates self._post and self._files if the content-type is a form type """ |
319 | 319 | if self.method != 'POST': |
320 | 320 | self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict() |
321 | 321 | return |
… |
… |
class HttpRequest(object):
|
323 | 323 | self._mark_post_parse_error() |
324 | 324 | return |
325 | 325 | |
326 | | if self.META.get('CONTENT_TYPE', '').startswith('multipart'): |
| 326 | if self.META.get('CONTENT_TYPE', '').startswith('multipart/form-data'): |
327 | 327 | if hasattr(self, '_body'): |
328 | 328 | # Use already read data |
329 | 329 | data = BytesIO(self._body) |
… |
… |
class HttpRequest(object):
|
341 | 341 | # empty POST |
342 | 342 | self._mark_post_parse_error() |
343 | 343 | raise |
344 | | else: |
| 344 | elif self.META.get('CONTENT_TYPE', '').startswith('application/x-www-form-urlencoded'): |
345 | 345 | self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict() |
| 346 | else: |
| 347 | self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict() |
346 | 348 | |
347 | 349 | ## File-like and iterator interface. |
348 | 350 | ## |
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index 0a337eb..860bfe1 100644
a
|
b
|
All attributes should be considered read-only, unless stated otherwise below.
|
92 | 92 | |
93 | 93 | .. attribute:: HttpRequest.POST |
94 | 94 | |
95 | | A dictionary-like object containing all given HTTP POST parameters. See the |
| 95 | A dictionary-like object containing all given HTTP POST parameters, |
| 96 | providing that the request contains form data. See the |
96 | 97 | :class:`QueryDict` documentation below. |
97 | 98 | |
| 99 | .. versionchanged:: 1.5 |
| 100 | Before Django 1.5, HttpRequest.POST did also contain non-form data. Now if |
| 101 | the ``Content-Type`` header is not ``multipart/form-data`` or |
| 102 | ``application/x-www-form-urlencoded``, you should access the posted data |
| 103 | through :attr:`HttpRequest.body` instead. |
| 104 | |
98 | 105 | It's possible that a request can come in via POST with an empty ``POST`` |
99 | 106 | dictionary -- if, say, a form is requested via the POST HTTP method but |
100 | 107 | does not include form data. Therefore, you shouldn't use ``if request.POST`` |
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
index d49bae8..b60761d 100644
a
|
b
|
For consistency with the design of the other generic views,
|
245 | 245 | dictionary into the context, instead passing the variables from the URLconf |
246 | 246 | directly into the context. |
247 | 247 | |
| 248 | Non-form data in HTTP requests |
| 249 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 250 | |
| 251 | :attr:`request.POST <django.http.HttpRequest.POST>` will no longer include data |
| 252 | posted via HTTP requests with non form-specific content-types in the header. |
| 253 | In prior versions, data posted with content-types other than |
| 254 | ``multipart/form-data`` or ``application/x-www-form-urlencoded`` would still |
| 255 | end up represented in the :attr:`request.POST <django.http.HttpRequest.POST>` |
| 256 | attribute. Developers wishing to access the raw POST data for these cases, |
| 257 | should use the :attr:`request.body <django.http.HttpRequest.body>` attribute |
| 258 | instead. |
| 259 | |
| 260 | For requests without a ``Content-Type`` header, this conforms to the HTTP |
| 261 | specification (:rfc:`2616`) which states that: *If the media type remains |
| 262 | unknown, the recipient SHOULD treat it as type "application/octet-stream"*. |
| 263 | |
248 | 264 | OPTIONS, PUT and DELETE requests in the test client |
249 | 265 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
250 | 266 | |
diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py
index d801613..378b4cf 100644
a
|
b
|
class RequestsTests(unittest.TestCase):
|
330 | 330 | def test_stream(self): |
331 | 331 | payload = b'name=value' |
332 | 332 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 333 | 'CONTENT_TYPE': 'application/x-www-form-urlencoded', |
333 | 334 | 'CONTENT_LENGTH': len(payload), |
334 | 335 | 'wsgi.input': BytesIO(payload)}) |
335 | 336 | self.assertEqual(request.read(), b'name=value') |
… |
… |
class RequestsTests(unittest.TestCase):
|
341 | 342 | """ |
342 | 343 | payload = b'name=value' |
343 | 344 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 345 | 'CONTENT_TYPE': 'application/x-www-form-urlencoded', |
344 | 346 | 'CONTENT_LENGTH': len(payload), |
345 | 347 | 'wsgi.input': BytesIO(payload)}) |
346 | 348 | self.assertEqual(request.POST, {'name': ['value']}) |
… |
… |
class RequestsTests(unittest.TestCase):
|
354 | 356 | """ |
355 | 357 | payload = b'name=value' |
356 | 358 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 359 | 'CONTENT_TYPE': 'application/x-www-form-urlencoded', |
357 | 360 | 'CONTENT_LENGTH': len(payload), |
358 | 361 | 'wsgi.input': BytesIO(payload)}) |
359 | 362 | self.assertEqual(request.read(2), b'na') |
… |
… |
class RequestsTests(unittest.TestCase):
|
402 | 405 | 'wsgi.input': BytesIO(payload)}) |
403 | 406 | self.assertEqual(request.POST, {}) |
404 | 407 | |
| 408 | def test_POST_binary_only(self): |
| 409 | payload = b'\r\n\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@' |
| 410 | environ = {'REQUEST_METHOD': 'POST', |
| 411 | 'CONTENT_TYPE': 'application/octet-stream', |
| 412 | 'CONTENT_LENGTH': len(payload), |
| 413 | 'wsgi.input': BytesIO(payload)} |
| 414 | request = WSGIRequest(environ) |
| 415 | self.assertEqual(request.POST, {}) |
| 416 | self.assertEqual(request.FILES, {}) |
| 417 | self.assertEqual(request.body, payload) |
| 418 | |
| 419 | # Same test without specifying content-type |
| 420 | environ.update({'CONTENT_TYPE': '', 'wsgi.input': BytesIO(payload)}) |
| 421 | request = WSGIRequest(environ) |
| 422 | self.assertEqual(request.POST, {}) |
| 423 | self.assertEqual(request.FILES, {}) |
| 424 | self.assertEqual(request.body, payload) |
| 425 | |
405 | 426 | def test_read_by_lines(self): |
406 | 427 | payload = b'name=value' |
407 | 428 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 429 | 'CONTENT_TYPE': 'application/x-www-form-urlencoded', |
408 | 430 | 'CONTENT_LENGTH': len(payload), |
409 | 431 | 'wsgi.input': BytesIO(payload)}) |
410 | 432 | self.assertEqual(list(request), [b'name=value']) |
… |
… |
class RequestsTests(unittest.TestCase):
|
415 | 437 | """ |
416 | 438 | payload = b'name=value' |
417 | 439 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 440 | 'CONTENT_TYPE': 'application/x-www-form-urlencoded', |
418 | 441 | 'CONTENT_LENGTH': len(payload), |
419 | 442 | 'wsgi.input': BytesIO(payload)}) |
420 | 443 | raw_data = request.body |
… |
… |
class RequestsTests(unittest.TestCase):
|
427 | 450 | """ |
428 | 451 | payload = b'name=value' |
429 | 452 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 453 | 'CONTENT_TYPE': 'application/x-www-form-urlencoded', |
430 | 454 | 'CONTENT_LENGTH': len(payload), |
431 | 455 | 'wsgi.input': BytesIO(payload)}) |
432 | 456 | raw_data = request.body |
… |
… |
class RequestsTests(unittest.TestCase):
|
479 | 503 | |
480 | 504 | payload = b'name=value' |
481 | 505 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 506 | 'CONTENT_TYPE': 'application/x-www-form-urlencoded', |
482 | 507 | 'CONTENT_LENGTH': len(payload), |
483 | 508 | 'wsgi.input': ExplodingBytesIO(payload)}) |
484 | 509 | |