Empty python request body - django

I am sending the below payload to the test api as shown but am getting an empty body. Please help. I have checked and I cant see where the problem is.
import requests
#(version 2.20.1)
requests.post('http://127.0.0.1:8000/api/test/',
json={'event_ref_id': 'PZpHoGrGKbIEv', 'member_no': 'mb001', 'action': 'course_registration'},
headers={'Content-Type': 'application/json'})
My Django endpoint is very simple as shown below but an getting an empty body
#api_view(['POST'])
def get_test(request):
print(request.body)
body_unicode = request.body.decode('utf-8')
body = json.loads(body_unicode)
print(body)
return Response(status=status.HTTP_200_OK)
Why API print body is
b'{}'
{}

Related

POST request parameters returning "none"

I have the following POST request:
import requests
payload = {'key1':'value1'}
r = requests.post('http://127.0.0.1:5000/test', params=payload)
print(r.url)
print(r.text)
My flask app tries to return the value from key1:
from flask import Flask, request
app = Flask(__name__)
#app.route('/test', methods = ["GET", "POST"])
def query_params():
val = request.args.get("key1")
return val
Going to http://127.0.0.1:5000/test returns
TypeError TypeError: The view function for 'query_params' did not
return a valid response. The function either returned None or ended
without a return statement.
Output from flask debugger:
127.0.0.1 - - [21/May/2022 21:47:17] "POST /test?key1=value1 HTTP/1.1" 200 -
What am I missing here? Thank you very much for your help!
Cheers,
Mario
when you visit the http://127.0.0.1:5000/test from your browser, its a GET request and there are no parameters passed in your request.
if you visit http://127.0.0.1:5000/test?key1=value1, your expected output will be printed.
Regarding the requests.post snippet you used: if you see the documentation, params is usually used in GET requests, the POSTS get the data argument. but seems your code works, it appends the parameters to the request (as would have happened in GET) and makes a POST request. Interesting finding!
r = requests.post('http://127.0.0.1:8000/test', data=payload)
you could enhance your code by using a "fallback" value if the parameter is not present:
#app.route('/test', methods = ["GET", "POST"])
def query_params():
val = request.args.get("key1", "parameter was not provided")
return val
To conclude, i think you should decide if the request method to submit the data should be a GET or a POST, and then update your code accordingly (if GET, your snippets is OK, if you should use POST, try to switch the params to data and then your flask route code to work with the new payload "format".
updated code to "launch a python script if the flask app receives a POST request with a specific key:value pair":
#app.route('/test', methods = ["GET", "POST"])
def query_params():
if request.method == 'POST':
val = request.args.get("the expected key", "parameter was not provided")
if val == "the expected value":
# do the things you want to do
return "processing done!"

Data in request.body can't be found by request.data - Django Rest Framework

I'm writing a django application. I am trying to call my django rest framework from outside, and expecting an answer.
I use requests to send some data to a function in the DRF like this:
j=[i.json() for i in AttachmentType.objects.annotate(text_len=Length('terms')).filter(text_len__gt=1)]
j = json.dumps(j)
url = settings.WEBSERVICE_URL + '/api/v1/inference'
headers = {
'Content-Disposition': f'attachment; filename={file_name}',
'callback': 'http://localhost',
'type':j,
'x-api-key': settings.WEBSERVICE_API_KEY
}
data = {
'type':j
}
files = {
'file':file
}
response = requests.post(
url,
headers=headers,
files=files,
json=data,
)
In the DRF, i use the request object to get the data.
class InferenceView(APIView):
"""
From a pdf file, extract infos and return it
"""
permission_classes = [HasAPIKey]
def post(self, request):
print("REQUEST FILE",request.FILES)
print("REQUEST DATA",request.data)
callback = request.headers.get('callback', None)
# check correctness of callback
msg, ok = check_callback(callback)
if not ok: # if not ok return bad request
return build_json_response(msg, 400)
# get zip file
zip_file = request.FILES.get('file', None)
parsed = json.loads(request.data.get('type', None).replace("'","\""))
The problem is that the data in the DRF are not received correctly. Whatever I send from the requests.post is not received.
I am sending a file and a JSON together. The file somehow is received, but other data are not.
If I try to do something like
request.data.update({"type":j})
in the DRF, the JSON is correctly added to the data, so it is not a problem with the JSON I'm trying to send itself.
Another thing, request.body shows that the JSON is somehow present in the body, but request.data can't find it.
I don't want to use request.body directly because I can't understand why it is present in the body but not visible with request.data.
In this line
response = requests.post(
url,
headers=headers,
files=files,
json=data,
)
replace json=data with data=data
like this:
response = requests.post(
url,
headers=headers,
files=files,
data=data,
)

Why i am getting 400 Bad Request error when sending json data in Flask?

I am trying to write a small restful api application, i am using Chrome Postman extension for sending requests to the app .
I believe that my code does not have mistakes but every time i am sending post request a 400 Bad Request error raising , here is my code:
#api_route.route('/api', methods=['GET'])
def api():
return jsonify({'message':'Api v1.0'})
#api_route.route('/api', methods=['POST'])
def create_user():
data = request.get_json()
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message', 'New User Created!'})
The json data that i am sending looks like this:
{"email" : "Test", "password" : "123123123"}
Why i am getting the 400 error ??
Update:
Screenshots for the requests using Postman:
GET Request
POST Request
Here i am initiating api route inside api controller :
from flask import Blueprint
api_route = Blueprint(
'api',
__name__
)
from . import views
then i am registering it inside def create_app() function :
from .api import api_route
app.register_blueprint(api_route)
Here are the extensions that i am using in my application:
toolbar = DebugToolbarExtension()
assets_env = Environment()
cache = Cache()
moment = Moment()
htmlminify = HTMLMIN()
csrf = CSRFProtect()
jac = JAC()
googlemap = GoogleMaps()
session = Session()
principal = Principal()
I solved the problem, i've initiated CSRFProtect with app so i need to include X-CSRFToken in all my requests, so i have two choices:
1 - To include the csrf_token in request.headers for all the requests
2 - Using #csrf.exempt decorator that coming with flask_wtf.csrf
For now i am using #csrf.exempt, so it become like this:
#api_route.route('/api', methods=['GET','POST'])
#csrf.exempt
def create_user():
if request.method == 'GET':
return jsonify({'message' : 'API v1.0'})
elif request.method == 'POST':
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user_api = Api(email=data['email'], password=hashed_password)
db.session.add(new_user_api)
db.session.commit()
return jsonify({'message' : 'New user created!'})
return return jsonify({'message' : 'No user has been added!'})
Thanks for #MrPyCharm for his interests , salute :) .
A good approach would be to structure your views as follows:
Instead of creating view with same route for different request methods, you can handle the request methods in the same view:
#api_route.route('/api', methods=['GET', 'POST'])
def api():
if request.method == 'GET':
return jsonify({'message':'Api v1.0'})
else:
data = request.get_json(force=True)
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message': 'New User Created!'})
# Just in case the if condition didn't satisfy
return None
A note for anyone else experiencing this with PostMan and Flask - you will also hit a HTTP 404 if your URL in PostMan is HTTPS but your Flask app only handles HTTP.

django and python requests - getting a 403 on a post request

I am using requests to log into my Django site for testing (and yes, I know about the Django TestClient, but I need plain http here). I can log in and, as long as I do get requests, everything is OK.
When I try to use post instead, I get a 403 from the csrf middleware. I've worked around that for now by using a #crsf_exempt on my view, but would prefer a longer term solution.
This is my code:
with requests.Session() as ses:
try:
data = {
'username': self.username,
'password': self.password,
}
ses.get(login_url)
try:
csrftoken = ses.cookies["csrftoken"]
except Exception, e:
raise
data.update(csrfmiddlewaretoken=csrftoken)
_login_response = ses.post(login_url, data=data)
logger.info("ses.cookies:%s" % (ses.cookies))
assert 200 <= _login_response.status_code < 300, "_login_response.status_code:%s" % (_login_response.status_code)
response = ses.post(
full_url,
data=data,
)
return self._process_response(response)
The login works fine, and I can see the csrf token here.
INFO:tests.helper_fetch:ses.cookies:<RequestsCookieJar[<Cookie csrftoken=TmM97gnNHs4YCgQPzfNztrAWY3KcysAg for localhost.local/>, <Cookie sessionid=kj6wfmta
However, the middleware sees cookies as empty.
INFO:django.middleware.csrf:request.COOKIES:{}
I've added the logging code to it:
def process_view(self, request, callback, callback_args, callback_kwargs):
if getattr(request, 'csrf_processing_done', False):
return None
try:
csrf_token = _sanitize_token(
request.COOKIES[settings.CSRF_COOKIE_NAME])
# Use same token next time
request.META['CSRF_COOKIE'] = csrf_token
except KeyError:
# import pdb
# pdb.set_trace()
import logging
logger = logging.getLogger(__name__)
logger.info("request.COOKIES:%s" % (request.COOKIES))
Am I missing something with way I call request's session.post? I tried adding cookie to it, made no difference. But I can totally see why crsf middleware is bugging out. I thought the cookies were part of the session, so why are they missing in my second post?
response = ses.post(
self.res.full_url,
data=data,
cookies=ses.cookies,
)
This variation, inspired by How to send cookies in a post request with the Python Requests library?, also did not result in anything being passed to csrf middleware:
response = ses.post(
self.res.full_url,
data=data,
cookies=dict(csrftoken=csrftoken),
)
For subsequent requests after the login, try supplying it as header X-CSRFToken instead.
The following worked for me:
with requests.Session() as sesssion:
response = session.get(login_url)
response.raise_for_status() # raises HTTPError if: 400 <= status_code < 600
csrf = session.cookies['csrftoken']
data = {
'username': self.username,
'password': self.password,
'csrfmiddlewaretoken': csrf
}
response = session.post(login_url, data=data)
response.raise_for_status()
headers = {'X-CSRFToken': csrf, 'Referer': url}
response = session.post('another_url', data={}, headers=headers)
response.raise_for_status()
return response # At this point we probably made it
Docs reference: https://docs.djangoproject.com/en/dev/ref/csrf/#csrf-ajax
You could also try to use this decorator on your view, instead of the csrf_exempt. I tried to reproduce your issue, and this worked as well for me.
from django.views.decorators.csrf import ensure_csrf_cookie`
#ensure_csrf_cookie
def your_login_view(request):
# your view code

OAuth authentication failed on PUT

I am using OAuth within my project. but I have come into an authentication problem.
It is that I can pass the authentification mechanism of Oauth with the method "POST" but not the method "PUT". The only difference between the POST and PUT request is the method type. The body and the header is the same. The requests I used are as follows :
POST
resp, cont = client.request("http://localhost:8000/api/1.0/booking/",
"POST",
data_booking,
headers=headers)
PUT
resp, cont = client.request("http://localhost:8000/api/1.0/booking/",
"PUT",
data_booking,
headers=headers)
The client is a OAuth client.
The error message returned by server is :
Fyi : 401 Unauthorized
Similar to 403 Forbidden, but specifically for use when authentication is possible but has failed or not yet been provided
I am developing using the django framework.
The request method is as follow :
def request(self, uri, method="GET", body=None, headers=None,
redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None,
callback_url=None, realm=''):
DEFAULT_CONTENT_TYPE = 'application/x-www-form-urlencoded'
if not isinstance(headers, dict):
headers = {}
is_multipart = method == 'POST' and headers.get('Content-Type',
DEFAULT_CONTENT_TYPE) != DEFAULT_CONTENT_TYPE
if body and (method == "POST" or method == 'PUT') and not is_multipart:
parameters = dict(parse_qsl(body))
if callback_url != None:
parameters['oauth_callback'] = callback_url
else:
if callback_url != None and not is_multipart:
parameters = {'oauth_callback': callback_url}
else:
parameters = None
req = Request.from_consumer_and_token(self.consumer,
token=self.token, http_method=method, http_url=uri,
parameters=parameters)
req.sign_request(self.method, self.consumer, self.token)
if method == "POST" or method == "PUT":
headers['Content-Type'] = headers.get('Content-Type',
DEFAULT_CONTENT_TYPE)
if is_multipart:
headers.update(req.to_header(realm))
else:
body = req.to_postdata()
elif method == "GET":
uri = req.to_url()
else:
headers.update(req.to_header(realm))
return httplib2.Http.request(self, uri, method=method, body=body,
headers=headers, redirections=redirections,
connection_type=connection_type)
Anyone has an idea?
Some OAuth server implementations only include form-encoded body parameters in the signature base string when the HTTP method is POST. This was the right behavior in OAuth 1.0 but was corrected in later revisions. Try making a PUT request without a body and see if that helps. If it does, you will need to ask the server library maintainer to fix this or limit your calls not to include a form-encoded body when using a put.