Flask - Getting request parameters in Google App Engine? - python

I'm running Flask on App Engine and I'm having trouble getting the request parameters of an issued request.
Here's my function being called:
#APP.route('/some_model/new', methods = ['PUT'])
def new_some_model():
prop1 = request.args.get('prop1')
prop2 = request.args.get('prop2')
import logging
logging.error(prop1)
logging.error(prop2)
And then I'm running the following command with curl:
curl -X PUT -d "prop1=prop1&prop2=prop2" http://myapp.appspot.com/some_function/new
I've tried several variations, but with no success. The curl command returns a "500 server error" and in the app-log I see that both prop1 and prop2 are None at the point of logging. The server error came from the properties being required later on. So the problem is that the request.args.get() is returning nothing. Any suggestions on what I might be doing wrong? Thank you much!

Data sent by POST or PUT will be in request.form, request.args is for parsed query string data.

Related

Flask App deployed on Heroku returns error on post requests

I deployed my Flask App on Heroku, I noticed that once deployed post request in the API don't work anymore and the app just responds with status code 400 and "message": "The browser (or proxy) sent a request that this server could not understand.". Get requests work normally but post don't. Also I am quite confused because if I run the app on localhost everything works as expected but not once deployed. Anyone knows how to solve?
To parse request argument I use args = ParseArgs.parse_args() from flask_restful and I retrive the value with:
args = ParseArgs.parse_args()
mode = args['Mode']
value = args['Value']
I tried to look up on google and I saw that it might be the Content-Type header with value application/json, I removed it but the problem still persists.

Django service on gunicorn POST request is recieved as GET?

I have a Django rest service running on virutal environment on gunicorn server with the following .wsgi file:
import os, sys import site
site.addsitedir('/opt/valuation/env/lib/python2.7/site-packages')
sys.stdout = sys.stderr
os.environ['DJANGO_SETTINGS_MODULE'] = 'valuation.valuationcont.valuation.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
When I do curl POST call the service works perfectly:
curl -H "Content-Type: application/json" -X POST -d '{...}' -u username:password http://localhost:8000/valuation/predict/
But when I do the same request on API gateway using axios, Django service responds my custom GET response ("GET not supported, try POST").
axios({
method: 'post',
url:'http://localhost:8000/valuation/predict',
headers:{
"Content-Type":"application/json",
"Authorization":"Basic [BASE64 ENCODING]"
},
data:{
...
}
}).then(response=>{
console.log(response.data)
}).catch(err=>{
console.log(err.toString())
})
The request is transformed from GET to POST.
This only happens with the django/gunicorn service.
Since I am new to django/gunicorn I think there is something wrong with the .wsgi file. But how come the curl call then works?
Any help appreciated, been struggling with this for a week now.
Edit:
Managed to recreate the same problem in my local machine. axios POST requests using its API are translated into GET.
Using the axios.post(...) method I managed to get 403 and 201. All while POSTMAN works fine.
I have a suspicion that since the POST fails axios API has a default fallback to GET which then doesn't fail and service responds normally ("GET not supported" as is should).
New step to debug this would be to ask, how do I recreate POSTMAN POST call as close as possible in javascript since POSTMAN is working and it is obviously axios that is causing the problems.
You're not using the same URL. In the curl snippet you request http://localhost:8000/valuation/predict/ but in the second you request http://localhost:8000/valuation/predict - without the final slash.
Django by default redirects URLs that don't end in a slash to one that does, and a redirect is always a GET.

How do parse a curl PUT request in Flask-RESTful?

How do I save the data uploaded using a curl command like curl localhost:5000/upload/test.bin --upload-file tmp/test.bin in a Flask-RESTful PUT handler method?
The code in Ron Harlev's answer to Flask-RESTful - Upload image works with the POST request from curl -F "file=#tmp/test.bin" localhost:5000/upload/test.bin (slightly modified below):
def post(self, filepath):
parse = reqparse.RequestParser()
parse.add_argument('file', type=werkzeug.datastructures.FileStorage, location='files')
args = parse.parse_args()
upload_file = args['file']
upload_file.save("/usr/tmp/{}".format(filepath))
return ({'filepath': filepath}, 200)
But if I try to use the code to handle the PUT request from curl --upload-file (changing post to put above, of course) I get: "'NoneType' object has no attribute 'save'". This refers to the second-last line in the above code.
How do I get a handle to the file data uploaded using curl --upload-file, so I can save it to a local file?
Update: This works around the problem: curl --request PUT -F "file=#tmp/test.bin" localhost:5000/upload/test.bin, but I still don't have an answer to my question.
curls docs define --upload-file as a PUT http request https://curl.haxx.se/docs/manpage.html#-T.
I'm not sure of the need to handle this through a restful API and I'm pretty sure that's where curl is causing problems, perhaps the assumption that this must be done through flask-restful is holding you back?
maybe try building this as a vanilla flask endpoint instead, this code should work for you.
from flask import Flask, request, jsonify
...
#app.route('/simpleupload/<string:filepath>', methods=['POST','PUT'])
def flask_upload(filepath):
with open("/tmp/{}".format(filepath), 'wb') as file:
file.write(request.stream.read())
return (jsonify({'filepath': filepath}), 200)

Flask JSON request is None

I'm working on my first Flask app (version 0.10.1), and also my first Python (version 3.5) app. One of its pieces needs to work like this:
Submit a form
Run a Celery task (which makes some third-party API calls)
When the Celery task's API calls complete, send a JSON post to another URL in the app
Get that JSON data and update a database record with it
Here's the relevant part of the Celery task:
if not response['errors']: # response comes from the Salesforce API call
# do something to notify that the task was finished successfully
message = {'flask_id' : flask_id, 'sf_id' : response['id']}
message = json.dumps(message)
print('call endpoint now and update it')
res = requests.post('http://0.0.0.0:5000/transaction_result/', json=message)
And here's the endpoint it calls:
#app.route('/transaction_result/', methods=['POST'])
def transaction_result():
result = jsonify(request.get_json(force=True))
print(result.flask_id)
return result.flask_id
So far I'm just trying to get the data and print the ID, and I'll worry about the database after that.
The error I get though is this: requests.exceptions.ConnectionError: None: Max retries exceeded with url: /transaction_result/ (Caused by None)
My reading indicates that my data might not be coming over as JSON, hence the Force=True on the result, but even this doesn't seem to work. I've also tried doing the same request in CocoaRestClient, with a Content-Type header of application/json, and I get the same result.
Because both of these attempts break, I can't tell if my issue is in the request or in the attempt to parse the response.
First of all request.get_json(force=True) returns an object (or None if silent=True). jsonify converts objects to JSON strings. You're trying to access str_val.flask_id. It's impossible. However, even after removing redundant jsonify call, you'll have to change result.flask_id to result['flask_id'].
So, eventually the code should look like this:
#app.route('/transaction_result/', methods=['POST'])
def transaction_result():
result = request.get_json()
return result['flask_id']
And you are absolutely right when you're using REST client to test the route. It crucially simplifies testing process by reducing involved parts. One well-known problem during sending requests from a flask app to the same app is running this app under development server with only one thread. In such case a request will always be blocked by an internal request because the current thread is serving the outermost request and cannot handle the internal one. However, since you are sending a request from the Celery task, it's not likely your scenario.
UPD: Finally, the last one reason was an IP address 0.0.0.0. Changing it to the real one solved the problem.

How can I get uwsgi to accept POST request without data?

I have a web application that serves post requests, and handles cases when no data or empty data are given. When running the application manually, it behaves properly. But when serving it through uwsgi, a specific case fails, POST requests with no data (empty data works):
$ curl -X POST http://www.example.com/search
curl: (52) Empty reply from server
$ curl -d "" http://www.example.com/search
[{...}]
Is there any uwsgi option I missed that could fix this behavior? How can I get uwsgi to properly forward those requests to my web application?
As #roberto stated, it was a problem in my code. I had this piece of code:
When a user does a post query without data, wheezy will try building self.request.form using environ variables like CONTENT_LENGTH, which here doesn't exist and raises an error. Somehow, the try/except did not work with uwsgi, so I changed:
try:
self.request.form
except:
self.request.form = {}
self.request.files = {}
As follow:
if not self.request.environ.get('CONTENT_LENGTH', False):
self.request.form = {}
self.request.files = {}
And it works fine now

Categories