diff --git a/django/http/__init__.py b/django/http/__init__.py
index 476a625..bb1c3b6 100644
|
a
|
b
|
|
| 4 | 4 | import os |
| 5 | 5 | import re |
| 6 | 6 | import time |
| | 7 | import warnings |
| | 8 | |
| 7 | 9 | from pprint import pformat |
| 8 | 10 | from urllib import urlencode, quote |
| 9 | 11 | from urlparse import urljoin |
| … |
… |
def parse_file_upload(self, META, post_data):
|
| 300 | 302 | parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding) |
| 301 | 303 | return parser.parse() |
| 302 | 304 | |
| 303 | | def _get_raw_post_data(self): |
| 304 | | if not hasattr(self, '_raw_post_data'): |
| | 305 | @property |
| | 306 | def body(self): |
| | 307 | if not hasattr(self, '_body'): |
| 305 | 308 | if self._read_started: |
| 306 | | raise Exception("You cannot access raw_post_data after reading from request's data stream") |
| 307 | | self._raw_post_data = self.read() |
| 308 | | self._stream = StringIO(self._raw_post_data) |
| 309 | | return self._raw_post_data |
| 310 | | raw_post_data = property(_get_raw_post_data) |
| | 309 | raise Exception("You cannot access body after reading from request's data stream") |
| | 310 | self._body = self.read() |
| | 311 | self._stream = StringIO(self._body) |
| | 312 | return self._body |
| | 313 | |
| | 314 | @property |
| | 315 | def raw_post_data(self): |
| | 316 | warnings.warn( |
| | 317 | 'The raw_post_data attribute has been renamed to body and the original ' |
| | 318 | 'name has been deprecated.', PendingDeprecationWarning) |
| | 319 | return self.body |
| 311 | 320 | |
| 312 | 321 | def _mark_post_parse_error(self): |
| 313 | 322 | self._post = QueryDict('') |
| … |
… |
def _load_post_and_files(self):
|
| 319 | 328 | if self.method != 'POST': |
| 320 | 329 | self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict() |
| 321 | 330 | return |
| 322 | | if self._read_started and not hasattr(self, '_raw_post_data'): |
| | 331 | if self._read_started and not hasattr(self, '_body'): |
| 323 | 332 | self._mark_post_parse_error() |
| 324 | 333 | return |
| 325 | 334 | |
| 326 | 335 | if self.META.get('CONTENT_TYPE', '').startswith('multipart'): |
| 327 | | if hasattr(self, '_raw_post_data'): |
| | 336 | if hasattr(self, '_body'): |
| 328 | 337 | # Use already read data |
| 329 | | data = StringIO(self._raw_post_data) |
| | 338 | data = StringIO(self._body) |
| 330 | 339 | else: |
| 331 | 340 | data = self |
| 332 | 341 | try: |
| … |
… |
def _load_post_and_files(self):
|
| 342 | 351 | self._mark_post_parse_error() |
| 343 | 352 | raise |
| 344 | 353 | else: |
| 345 | | self._post, self._files = QueryDict(self.raw_post_data, encoding=self._encoding), MultiValueDict() |
| | 354 | self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict() |
| 346 | 355 | |
| 347 | 356 | ## File-like and iterator interface. |
| 348 | 357 | ## |
| 349 | 358 | ## Expects self._stream to be set to an appropriate source of bytes by |
| 350 | 359 | ## a corresponding request subclass (WSGIRequest or ModPythonRequest). |
| 351 | 360 | ## Also when request data has already been read by request.POST or |
| 352 | | ## request.raw_post_data, self._stream points to a StringIO instance |
| | 361 | ## request.body, self._stream points to a StringIO instance |
| 353 | 362 | ## containing that data. |
| 354 | 363 | |
| 355 | 364 | def read(self, *args, **kwargs): |
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 91fde96..9fcb681 100644
|
a
|
b
|
these changes.
|
| 257 | 257 | * Setting the ``is_safe`` and ``needs_autoescape`` flags as attributes of |
| 258 | 258 | template filter functions will no longer be supported. |
| 259 | 259 | |
| | 260 | * The attribute ``HttpRequest.raw_post_data`` was renamed to ``HttpRequest.body`` |
| | 261 | in 1.4. The backwards compatibility shim will be removed. |
| | 262 | |
| 260 | 263 | 2.0 |
| 261 | 264 | --- |
| 262 | 265 | |
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index 64d0e10..8c2b7a6 100644
|
a
|
b
|
Attributes
|
| 30 | 30 | |
| 31 | 31 | All attributes except ``session`` should be considered read-only. |
| 32 | 32 | |
| | 33 | .. attribute:: HttpRequest.body |
| | 34 | |
| | 35 | .. versionchanged:: 1.4 |
| | 36 | Prior to 1.4, ``HttpRequest.body`` was named ``HttpRequest.raw_post_data``. |
| | 37 | |
| | 38 | The raw HTTP request body as a byte string. This is useful for processing |
| | 39 | data in different formats than of conventional HTML forms: binary images, |
| | 40 | XML payload etc. For processing form data use ``HttpRequest.POST``. |
| | 41 | |
| | 42 | .. versionadded:: 1.3 |
| | 43 | |
| | 44 | You can also read from an HttpRequest using file-like interface. See |
| | 45 | :meth:`HttpRequest.read()`. |
| | 46 | |
| 33 | 47 | .. attribute:: HttpRequest.path |
| 34 | 48 | |
| 35 | 49 | A string representing the full path to the requested page, not including |
| … |
… |
All attributes except ``session`` should be considered read-only.
|
| 170 | 184 | support activated. See the :doc:`session documentation |
| 171 | 185 | </topics/http/sessions>` for full details. |
| 172 | 186 | |
| 173 | | .. attribute:: HttpRequest.raw_post_data |
| 174 | | |
| 175 | | The raw HTTP POST data as a byte string. This is useful for processing |
| 176 | | data in different formats than of conventional HTML forms: binary images, |
| 177 | | XML payload etc. For processing form data use ``HttpRequest.POST``. |
| 178 | | |
| 179 | | .. versionadded:: 1.3 |
| 180 | | |
| 181 | | You can also read from an HttpRequest using file-like interface. See |
| 182 | | :meth:`HttpRequest.read()`. |
| 183 | | |
| 184 | 187 | .. attribute:: HttpRequest.urlconf |
| 185 | 188 | |
| 186 | 189 | Not defined by Django itself, but will be read if other code (e.g., a custom |
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
index f614dee..385010b 100644
|
a
|
b
|
useful, it was removed in Django 1.4. If you relied on it, you must edit your
|
| 995 | 995 | settings file to list all your applications explicitly. |
| 996 | 996 | |
| 997 | 997 | .. _this can't be done reliably: http://docs.python.org/tutorial/modules.html#importing-from-a-package |
| | 998 | |
| | 999 | ``HttpRequest.raw_post_data renamed to HttpRequest.body`` |
| | 1000 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | 1001 | This attribute was named ``HttpRequest.raw_post_data`` but what it actually provided |
| | 1002 | was the body of the HTTP request. It has been renamed to ``HttpRequest.body`` to |
| | 1003 | better match what it actually provides and ``HttpRequest.raw_post_data`` has been |
| | 1004 | deprecated. |
diff --git a/tests/modeltests/test_client/views.py b/tests/modeltests/test_client/views.py
index a86064e..6ea7213 100644
|
a
|
b
|
def raw_post_view(request):
|
| 44 | 44 | """A view which expects raw XML to be posted and returns content extracted |
| 45 | 45 | from the XML""" |
| 46 | 46 | if request.method == 'POST': |
| 47 | | root = parseString(request.raw_post_data) |
| | 47 | root = parseString(request.body) |
| 48 | 48 | first_book = root.firstChild.firstChild |
| 49 | 49 | title, author = [n.firstChild.nodeValue for n in first_book.childNodes] |
| 50 | 50 | t = Template("{{ title }} - {{ author }}", name="Book template") |
diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py
index e96f312..7927d27 100644
|
a
|
b
|
|
| 1 | 1 | import time |
| | 2 | import warnings |
| 2 | 3 | from datetime import datetime, timedelta |
| 3 | 4 | from StringIO import StringIO |
| 4 | 5 | |
| … |
… |
|
| 6 | 7 | from django.core.handlers.modpython import ModPythonRequest |
| 7 | 8 | from django.core.handlers.wsgi import WSGIRequest, LimitedStream |
| 8 | 9 | from django.http import HttpRequest, HttpResponse, parse_cookie, build_request_repr |
| | 10 | from django.test.utils import get_warnings_state, restore_warnings_state |
| 9 | 11 | from django.utils import unittest |
| 10 | 12 | from django.utils.http import cookie_date |
| 11 | 13 | |
| … |
… |
def test_stream(self):
|
| 294 | 296 | def test_read_after_value(self): |
| 295 | 297 | """ |
| 296 | 298 | Reading from request is allowed after accessing request contents as |
| 297 | | POST or raw_post_data. |
| | 299 | POST or body. |
| 298 | 300 | """ |
| 299 | 301 | payload = 'name=value' |
| 300 | 302 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 301 | 303 | 'CONTENT_LENGTH': len(payload), |
| 302 | 304 | 'wsgi.input': StringIO(payload)}) |
| 303 | 305 | self.assertEqual(request.POST, {u'name': [u'value']}) |
| 304 | | self.assertEqual(request.raw_post_data, 'name=value') |
| | 306 | self.assertEqual(request.body, 'name=value') |
| 305 | 307 | self.assertEqual(request.read(), 'name=value') |
| 306 | 308 | |
| 307 | 309 | def test_value_after_read(self): |
| 308 | 310 | """ |
| 309 | | Construction of POST or raw_post_data is not allowed after reading |
| | 311 | Construction of POST or body is not allowed after reading |
| 310 | 312 | from request. |
| 311 | 313 | """ |
| 312 | 314 | payload = 'name=value' |
| … |
… |
def test_value_after_read(self):
|
| 314 | 316 | 'CONTENT_LENGTH': len(payload), |
| 315 | 317 | 'wsgi.input': StringIO(payload)}) |
| 316 | 318 | self.assertEqual(request.read(2), 'na') |
| 317 | | self.assertRaises(Exception, lambda: request.raw_post_data) |
| | 319 | self.assertRaises(Exception, lambda: request.body) |
| 318 | 320 | self.assertEqual(request.POST, {}) |
| 319 | 321 | |
| 320 | | def test_raw_post_data_after_POST_multipart(self): |
| | 322 | def test_body_after_POST_multipart(self): |
| 321 | 323 | """ |
| 322 | | Reading raw_post_data after parsing multipart is not allowed |
| | 324 | Reading body after parsing multipart is not allowed |
| 323 | 325 | """ |
| 324 | 326 | # Because multipart is used for large amounts fo data i.e. file uploads, |
| 325 | 327 | # we don't want the data held in memory twice, and we don't want to |
| 326 | | # silence the error by setting raw_post_data = '' either. |
| | 328 | # silence the error by setting body = '' either. |
| 327 | 329 | payload = "\r\n".join([ |
| 328 | 330 | '--boundary', |
| 329 | 331 | 'Content-Disposition: form-data; name="name"', |
| … |
… |
def test_raw_post_data_after_POST_multipart(self):
|
| 336 | 338 | 'CONTENT_LENGTH': len(payload), |
| 337 | 339 | 'wsgi.input': StringIO(payload)}) |
| 338 | 340 | self.assertEqual(request.POST, {u'name': [u'value']}) |
| 339 | | self.assertRaises(Exception, lambda: request.raw_post_data) |
| | 341 | self.assertRaises(Exception, lambda: request.body) |
| 340 | 342 | |
| 341 | 343 | def test_POST_multipart_with_content_length_zero(self): |
| 342 | 344 | """ |
| … |
… |
def test_read_by_lines(self):
|
| 366 | 368 | 'wsgi.input': StringIO(payload)}) |
| 367 | 369 | self.assertEqual(list(request), ['name=value']) |
| 368 | 370 | |
| 369 | | def test_POST_after_raw_post_data_read(self): |
| | 371 | def test_POST_after_body_read(self): |
| 370 | 372 | """ |
| 371 | | POST should be populated even if raw_post_data is read first |
| | 373 | POST should be populated even if body is read first |
| 372 | 374 | """ |
| 373 | 375 | payload = 'name=value' |
| 374 | 376 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 375 | 377 | 'CONTENT_LENGTH': len(payload), |
| 376 | 378 | 'wsgi.input': StringIO(payload)}) |
| 377 | | raw_data = request.raw_post_data |
| | 379 | raw_data = request.body |
| 378 | 380 | self.assertEqual(request.POST, {u'name': [u'value']}) |
| 379 | 381 | |
| 380 | | def test_POST_after_raw_post_data_read_and_stream_read(self): |
| | 382 | def test_POST_after_body_read_and_stream_read(self): |
| 381 | 383 | """ |
| 382 | | POST should be populated even if raw_post_data is read first, and then |
| | 384 | POST should be populated even if body is read first, and then |
| 383 | 385 | the stream is read second. |
| 384 | 386 | """ |
| 385 | 387 | payload = 'name=value' |
| 386 | 388 | request = WSGIRequest({'REQUEST_METHOD': 'POST', |
| 387 | 389 | 'CONTENT_LENGTH': len(payload), |
| 388 | 390 | 'wsgi.input': StringIO(payload)}) |
| 389 | | raw_data = request.raw_post_data |
| | 391 | raw_data = request.body |
| 390 | 392 | self.assertEqual(request.read(1), u'n') |
| 391 | 393 | self.assertEqual(request.POST, {u'name': [u'value']}) |
| 392 | 394 | |
| 393 | | def test_POST_after_raw_post_data_read_and_stream_read_multipart(self): |
| | 395 | def test_POST_after_body_read_and_stream_read_multipart(self): |
| 394 | 396 | """ |
| 395 | | POST should be populated even if raw_post_data is read first, and then |
| | 397 | POST should be populated even if body is read first, and then |
| 396 | 398 | the stream is read second. Using multipart/form-data instead of urlencoded. |
| 397 | 399 | """ |
| 398 | 400 | payload = "\r\n".join([ |
| … |
… |
def test_POST_after_raw_post_data_read_and_stream_read_multipart(self):
|
| 406 | 408 | 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', |
| 407 | 409 | 'CONTENT_LENGTH': len(payload), |
| 408 | 410 | 'wsgi.input': StringIO(payload)}) |
| 409 | | raw_data = request.raw_post_data |
| | 411 | raw_data = request.body |
| 410 | 412 | # Consume enough data to mess up the parsing: |
| 411 | 413 | self.assertEqual(request.read(13), u'--boundary\r\nC') |
| 412 | 414 | self.assertEqual(request.POST, {u'name': [u'value']}) |
| | 415 | |
| | 416 | def test_raw_post_data_returns_body(self): |
| | 417 | """ |
| | 418 | HttpRequest.raw_post_body should be the same as HttpRequest.body |
| | 419 | """ |
| | 420 | payload = 'Hello There!' |
| | 421 | request = WSGIRequest({ |
| | 422 | 'REQUEST_METHOD': 'POST', |
| | 423 | 'CONTENT_LENGTH': len(payload), |
| | 424 | 'wsgi.input': StringIO(payload) |
| | 425 | }) |
| | 426 | |
| | 427 | warnings_state = get_warnings_state() |
| | 428 | warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.http') |
| | 429 | self.assertEqual(request.body, request.raw_post_data) |
| | 430 | restore_warnings_state(warnings_state) |
diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
index 7d0b0e4..4ddd957 100644
|
a
|
b
|
def test_response_no_template(self):
|
| 975 | 975 | |
| 976 | 976 | class ReadLimitedStreamTest(TestCase): |
| 977 | 977 | """ |
| 978 | | Tests that ensure that HttpRequest.raw_post_data, HttpRequest.read() and |
| | 978 | Tests that ensure that HttpRequest.body, HttpRequest.read() and |
| 979 | 979 | HttpRequest.read(BUFFER) have proper LimitedStream behaviour. |
| 980 | 980 | |
| 981 | 981 | Refs #14753, #15785 |
| 982 | 982 | """ |
| 983 | | def test_raw_post_data_from_empty_request(self): |
| 984 | | """HttpRequest.raw_post_data on a test client GET request should return |
| | 983 | |
| | 984 | def test_body_from_empty_request(self): |
| | 985 | """HttpRequest.body on a test client GET request should return |
| 985 | 986 | the empty string.""" |
| 986 | | self.assertEquals(self.client.get("/test_client_regress/raw_post_data/").content, '') |
| | 987 | self.assertEquals(self.client.get("/test_client_regress/body/").content, '') |
| 987 | 988 | |
| 988 | 989 | def test_read_from_empty_request(self): |
| 989 | 990 | """HttpRequest.read() on a test client GET request should return the |
diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py
index 93f7a2e..d869c23 100644
|
a
|
b
|
|
| 31 | 31 | (r'^parse_unicode_json/$', views.return_json_file), |
| 32 | 32 | (r'^check_headers/$', views.check_headers), |
| 33 | 33 | (r'^check_headers_redirect/$', RedirectView.as_view(url='/test_client_regress/check_headers/')), |
| 34 | | (r'^raw_post_data/$', views.raw_post_data), |
| | 34 | (r'^body/$', views.body), |
| 35 | 35 | (r'^read_all/$', views.read_all), |
| 36 | 36 | (r'^read_buffer/$', views.read_buffer), |
| 37 | 37 | (r'^request_context_view/$', views.request_context_view), |
diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py
index b398293..ebb68c4 100644
|
a
|
b
|
|
| | 1 | import warnings |
| | 2 | |
| 1 | 3 | from django.conf import settings |
| 2 | 4 | from django.contrib.auth.decorators import login_required |
| 3 | 5 | from django.http import HttpResponse, HttpResponseRedirect |
| … |
… |
def return_json_file(request):
|
| 79 | 81 | charset = settings.DEFAULT_CHARSET |
| 80 | 82 | |
| 81 | 83 | # This just checks that the uploaded data is JSON |
| 82 | | obj_dict = simplejson.loads(request.raw_post_data.decode(charset)) |
| | 84 | obj_dict = simplejson.loads(request.body.decode(charset)) |
| 83 | 85 | obj_json = simplejson.dumps(obj_dict, encoding=charset, |
| 84 | 86 | cls=DjangoJSONEncoder, |
| 85 | 87 | ensure_ascii=False) |
| … |
… |
def check_headers(request):
|
| 92 | 94 | "A view that responds with value of the X-ARG-CHECK header" |
| 93 | 95 | return HttpResponse('HTTP_X_ARG_CHECK: %s' % request.META.get('HTTP_X_ARG_CHECK', 'Undefined')) |
| 94 | 96 | |
| 95 | | def raw_post_data(request): |
| 96 | | "A view that is requested with GET and accesses request.raw_post_data. Refs #14753." |
| 97 | | return HttpResponse(request.raw_post_data) |
| | 97 | def body(request): |
| | 98 | "A view that is requested with GET and accesses request.body. Refs #14753." |
| | 99 | return HttpResponse(request.body) |
| 98 | 100 | |
| 99 | 101 | def read_all(request): |
| 100 | 102 | "A view that is requested with accesses request.read()." |