request.data in DRF vs request.body in Django - python

Django REST framework introduces a Request object that extends the regular HttpRequest, this new object type has request.data to access JSON data for 'POST', 'PUT' and 'PATCH' requests.
However, I can get the same data by accessing request.body parameter which was part of original Django HttpRequest type object.
One difference which I see is that request.data can be accessed only one time. This restriction doesnt apply to request.body.
My question is what are the differences between the two. What is preferred and what is the reason DRF provides an alternative way of doing same thing when There should be one-- and preferably only one --obvious way to do it.
UPDATE: Limiting the usecase where body is always of type JSON. Never XML/ image or conventional form data. What are pros/cons of each?

You should use request.data. It's more flexible, covers more use cases and it can be accessed as many times as needed. Quoting the docs:
Aboout request.data
REST framework introduces a Request object that extends the regular
HttpRequest, and provides more flexible request parsing. The core
functionality of the Request object is the request.data attribute,
which is similar to request.POST, but more useful for working with Web
APIs.
request.POST # Only handles form data. Only works for 'POST' method.
request.data # Handles arbitrary data. Works for 'POST', 'PUT' and
'PATCH' methods.
About request.body
The raw HTTP request body as a byte string. This is useful for
processing data in different ways than conventional HTML forms: binary
images, XML payload etc. For processing conventional form data, use
HttpRequest.POST.
So unless you want to handle binary images or XML payload, never use request.body, it'll only be a simple string containing, well, the body of the request. Always use request.data which'll be the fully parsed body (i.e. a Python dict) which is much more convenient to handle.

In rest_framework.request.Request
request.body is bytes, which is always available, thus there is no limit in usage
request.data is a "property" method and can raise an exception,
but it gives you parsed data, which are more convenient
However, the world is not perfect and here is a case when request.body win
Consider this example:
If client send:
content-type: text/plain
and your REST's endpoint doesn't accept text/plain
your server will return 415 Unsupported Media Type
if you access request.data
But what if you know that json.loads(request.body) is correct json.
So you want to use that and only request.body allow that.
FYI: A described example is a message of AWS SNS notification sent by AWS to HTTP endpoint. AWS SNS works as a client here and of course, this case is a bug in their SNS.
Another example of benefits from request.body is a case when you have own custom parsing and you use own MIME format.

Related

What error to raise with wrong post request body?

I am new to Django and have an endpoint that accepts post requests. The endpoint expects a specific set of keys in the post request body. What type of Exception should I raise if they don't provide the right keys? ValueError? Thanks!
You should return a HttpResponseBadRequest response and include a helpful message with the invalid data. This returns a 400 response which is used to indicate issues with request data
If you are creating rest endpoints with Django it is worth using a library like https://www.django-rest-framework.org/ as it can simplify their creation and handle a lot of the leg work around validation, serialisation and reponses

Django Rest API POST issues

I'm trying to build a very simple REST API in Django 1.8 with Django REST Framework in Visual Studio, in which I want to have a single service method to process a JSON, but I can't seem to make a POST:
I'm trying to send this simple JSON through Postman, just as a test:
{
"foo":"bar"
}
with the header:
Content-Type: application/json
Here's my method:
#csrf_exempt
#api_view(['POST'])
def test(request):
data = request.data
return HttpResponse(status=200)
But my problem is that request.data is empty. And if instead I try to access request.body, I get
You cannot access body after reading from request's data stream.
Any ideas what could be the issue here?
Figured this out somewhat, it seems to be an issue with Visual Studio while in debug mode. If I try to access the request while debugging before calling any Python function on it (such as a simple print, or passing in to a function to parse it), it shows up as an empty QueryDict, otherwise it shows up fine.
Just a guess: maybe the issue is in Postman?
Try to send POST-request without headers, but with raw JSON (not form-data):
This may help Where's my JSON data in my incoming Django request?
Outside of this, make sure the content-type and accept-type are set properly. What is the raw response in Postman? Is the security setup properly?
I have the same problem when using POSTMAN.
Solved and Credit goes to https://stackoverflow.com/a/31977373/764592
Quoted Answer:
Request payload is not converted into JSON format.
I am passing my data in Body as x-www-form-urlencoded
You can fix it by using Content-Type as application/x-www-form-urlencoded in request header.

POST parameters formatted as JSON

I am writing a web service using Django that will be consumed from a MS SharePoint workflow. In the SP workflow, I created a dictionary with 2 items (id:1, text:'foo'), and used this dictionary as the request content. However, instead of using the dictionary to format a traditional POST parameter list, it sends it as a JSON object in the body of the POST request, so instead of the expected:
id=1&text=foo
in the body of the request, there is this:
{"id":1,"text":"foo"}
which of course, in turn, does not get parsed correctly by Python/Django (I am not sure who exactly does the parsing). How can I either get it to parse JSON, or get SharePoint to send traditionally encoded POST parameters?
EDIT
I saw other posts that explain how to get the raw body and parse the JSON. I was looking for a solution that would either:
Make SharePoint send normal data, or
Get Django to respect the Content-type header that states the data is JSON
There is no need for any parsing at the framework level. The body of the post request is always available in request.body, so you can access it directly:
result = json.loads(request.body)
May be it will help you bit more to handle.
import json
import urlparse
json.dumps(urlparse.parse_qs("id=1&text=foo"))

Passing data in non-POST call

For a REST api, I know it is acceptable to pass data in a POST call:
if method == 'POST':
r = requests.post(url, headers=headers, data=body)
Is it acceptable to pass data in a PUT or DELETE call? Or are you not supposed to send any data parms, and only request the specified url?
RFC 7231 explains everything you need to know.
PUT is very similar to POST in a REST API.
POST
The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics. RFC 7231 #4.3.3
PUT
The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload. RFC 7231 #4.3.4
Both request data. Furthermore, the RFC explicitly highlights the difference, since it is indeed slight:
The fundamental difference between the POST and PUT methods is highlighted by the different intent for the enclosed representation. The target resource in a POST request is intended to handle the enclosed representation according to the resource's own semantics, whereas the enclosed representation in a PUT request is defined as replacing the state of the target resource. Hence, the intent of PUT is idempotent and visible to intermediaries, even though the exact effect is only known by the origin server.
Regarding DELETE, the RFC says the following:
A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request.
I presume that means that you shouldn't send data. Regardless, the RFC does mention
Relatively few resources allow the DELETE method
which is spot on, in my opinion. You should just avoid DELETE altogether.

Flask: Getting request parameters based on Content-Type header

What is the proper way to deal with the request body depending on the Content-Type header of the request?
I need to implement a RESTful service that supports XML, JSON and form encoded request parameters, but I can't seem to find a clean way of extracting the request parameters.
Is this something that I should use a middleware for? Do I need to extend the Request object?
I haven't found any packages that do this, and it seems like a pretty common task for creating RESTful services in flask.
You probably meant Accept header, as Content-Type is used for response. There are three choices here:
Build it youself as described in Handling Accept Headers snippet.
Use Flask-RESTful extension (consult Content Negotiation part in the docs).
Use Flask-Pushrod extension which is specifically built to handle this case.
You could use #app.before_request as illustrated here. Once you've done your thing normalizing the request params, you can save them to g, something like this:
from flask import g
from flask import request
...
#app.before_request
def before_request():
# normalize params based on Content-Type
g.params = normalized_params

Categories

Resources