I am calling a flask app that is hosted with AWS Elastic Beanstalk. The app takes in json, creates a pandas data frame from it, does some other processing and passes data into a model. The predictions are returned.
If I run the following, the code executes and returns back the proper response.
import requests
v=pandas_df.iloc[0:5].to_json(orient='records')
headers = {'content-type': 'application/json'}
r = requests.post('http://XXXXXXXXX.us-east-1.elasticbeanstalk.com/', json=v, headers = headers)
r.json() #the predictions
However I can not get the same result using curl or postman.
For curl I have tried to send the json object created by pandas:
pandas_df.iloc[0:5].to_json(orient='records',path_or_buf=jsonexp.json)
curl -X POST "http://XXXXXXXXX.us-east-1.elasticbeanstalk.com/" -H "Content-Type: application/json" -d "jsonexp.json"
but the result is
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>
I have tried passing the string version of v, for the sample of 5 records above
curl -X POST "http://XXXXXXXXX.us-east-1.elasticbeanstalk.com/" -H "Content-Type: application/json" -d '[{"field1":14239302,"field2":29....}]'
but it returns
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
How can I get the same result request through curl?
ADD
Here is a reproducible example. The issue seems to be in how the json is read for creation of a pandas data frame.
Here is the flask app:
from flask import Flask, jsonify, request, render_template, make_response
import pandas as pd
application = Flask(__name__)
#application.route('/', methods=['GET','POST'])
def apicall():
if request.method == 'POST':
f = request.get_json()
print(f)
print(type(f))
f=str(f)
print(type(f))
df=pd.read_json(f,orient='records')
print(df)
return(jsonify(df.to_json(orient='records')))
if __name__ == '__main__':
application.run()
This works perfectly:
r=requests.post('http://127.0.0.1:5000/', json='[{"field1":14239302,"field2":29.90}]', headers = {'content-type': 'application/json'})
r.json()
127.0.0.1 - - [13/Sep/2019 15:28:16] "POST / HTTP/1.1" 500 -
[{"field1":14239302,"field2":29.90}]
<class 'str'>
<class 'str'>
field1 field2
0 14239302 29.9
127.0.0.1 - - [13/Sep/2019 15:34:51] "POST / HTTP/1.1" 200 -
This fails:
!curl -d '[{"field1":14239302,"field2":29.90}]' -H "Content-Type: application/json" -X POST 'http://127.0.0.1:5000/'
with the following
127.0.0.1 - - [13/Sep/2019 15:28:13] "POST / HTTP/1.1" 200 -
{'field1': 14239302, 'field2': 29.9}
<class 'list'>
<class 'str'>
[2019-09-13 15:28:16,851] ERROR in app: Exception on / [POST]
Traceback (most recent call last):
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/flask/app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/flask/app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/flask/app.py", line 1820, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/flask/app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/flask/app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "application.py", line 17, in apicall
df=pd.read_json(f,orient='records')
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/pandas/io/json/_json.py", line 592, in read_json
result = json_reader.read()
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/pandas/io/json/_json.py", line 717, in read
obj = self._get_object_parser(self.data)
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/pandas/io/json/_json.py", line 739, in _get_object_parser
obj = FrameParser(json, **kwargs).parse()
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/pandas/io/json/_json.py", line 849, in parse
self._parse_no_numpy()
File "/home/ubuntu/env_india_flask_eb/lib/python3.6/site-packages/pandas/io/json/_json.py", line 1116, in _parse_no_numpy
loads(json, precise_float=self.precise_float), dtype=None
ValueError: Expected object or value
127.0.0.1 - - [13/Sep/2019 15:28:16] "POST / HTTP/1.1" 500 -
It seems like the issue is with the fact that the input to the flask app is a string for requests and a list for curl?
ADD BASED ON KEVIN's ANSWER:
I tried this function:
from flask import Flask, jsonify, request, render_template, make_response
import pandas as pd
application = Flask(__name__)
#application.route('/', methods=['GET','POST'])
def apicall():
if request.method == 'POST':
f = request.get_json(force=True)
print(f)
print(type(f))
df=pd.read_json(f,orient='records')
return(jsonify(df.to_json(orient='records')))
if __name__ == '__main__':
application.run()
and this input:
s=json.dumps([{'field1':14239302,'field2':29.90}])
!curl -d s -H "Content-Type:application/json" -X POST 'http://127.0.0.1:5000/'
But still receive an error:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>
I guess you are using ipython shell. s variable is not rendered unless you put it in curly brackets. Try
s=json.dumps([{'field1':14239302,'field2':29.90}])
!curl -d '{s}' -H "Content-Type:application/json" -X POST 'http://127.0.0.1:5000/'
s is not valid JSON hence 400 Error.
However the problem's cause is different.
In fact Flask get_json returns str when request is made by requests package or list is returned when using curl which causes 500 error.
This is because requests module escapes quote character and request's body looks this way: b'"[{\\"field1\\":14239302,\\"field2\\":29.90}]"' while body from curl is b'[{"field1": 14239302, "field2": 29.9}]'.
To make it work with curl you will need to escape " quote characters inside dict and put it in quotes like so:
curl -H "Content-Type: application/json" -d '"[{\"field1\":14239302,\"field2\":29.9}]"' -X POST 'http://127.0.0.1:5000/'`
pd.read_json requires a valid JSON string.
You are trying to use string casting to convert to JSON. str(f) is not a reliable way to convert to JSON.
The reason it works with requests is because you got lucky... you can see that it prints valid JSON:
[{"field1":14239302,"field2":29.90}]
However when you run curl, you do not have valid JSON due to the single quotes.
{'field1': 14239302, 'field2': 29.9}
Try using the json module to convert Python objects to a valid JSON string.
import json
json_string = json.dumps([{'field1': 14239302, 'field2': 29.9}])
print(json_string)
# '[{"field1":14239302,"field2":29.90}]'
# see how it prints your json_string with double quotes wrapping the keys
Related
This question already has answers here:
How to get POSTed JSON in Flask?
(13 answers)
Closed 6 years ago.
This is what i get
b'{"data": "https://files.slack.com/files-pri/T03HPFD2P-F2RU2S4R0/scher7-om-14.52.47.png"}'
when i print my request data from a flask POST web service.
print(request.data)
But when i do url = request.form.get('data', None)
I get value of url variable None
Why?
Your problem is that you sent the request as follows:
req = requests.post(url, data=json.dumps(payload), headers={'Content-Type': 'application/json'})
When sending json data, you should format the request in requests with the json parameter as follows:
req = requests.post(url, json=payload)
Further, in your flask app, you can access submitted json data via request.json
Because the content type is application/json -- it will not appear in request.form -- Data will appear in request.form when a request is received with an appropriate content type such as multipart/form-data
What's the Content-Type header in your POST request?
As described in its docs: request.form is a dict contains data parsed from POST or PUT form, while request.data contains the incoming request data as string in case it came with a mimetype Flask does not handle.
For the following code:
# -*- coding: utf-8 -*-
from flask import Flask, request
app = Flask("Test")
#app.route("/ping", methods=['POST'])
def ping():
print "Data: ", request.data
print "Form: ", request.form
return "pong"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7070, debug=False)
Curl with default Content-Type, there's no data in request.data:
curl -X POST -H "Content-Type: a/b" --data 'data=https://files.slack.com/files-pri/T03HPFD2P-F2RU2S4R0/schermafbeelding-2016-01-27-om-14.52.47.png' http://localhost:7070/ping
Output:
* Running on http://0.0.0.0:7070/ (Press CTRL+C to quit)
Data:
Form: ImmutableMultiDict([('data', u'https://files.slack.com/files-pri/T03HPFD2P-F2RU2S4R0/schermafbeelding-2016-01-27-om-14.52.47.png')])
127.0.0.1 - - [20/Oct/2016 21:40:41] "POST /ping HTTP/1.1" 200 -
But curl with a unknown Content-Type header, there's no data in request.form:
curl -X POST -H "Content-Type: a/b" --data 'data=https://files.slack.com/files-pri/T03HPFD2P-F2RU2S4R0/schermafbeelding-2016-01-27-om-14.52.47.png' http://localhost:7070/ping
Output:
Data: data=https://files.slack.com/files-pri/T03HPFD2P-F2RU2S4R0/schermafbeelding-2016-01-27-om-14.52.47.png
Form: ImmutableMultiDict([])
127.0.0.1 - - [20/Oct/2016 21:43:52] "POST /ping HTTP/1.1" 200 -
So if you want your form data in request.form, make sure the Content-Type is the one of those Flask can handle.
I have spent about a week on this issue and although I have made considerable progress I am stuck at a key point.
I am writing a simple client-server program in Python that is supposed to accept key/value pairs from the command line, formulate them into an url, and request the url from the server. The problem appears to be either that the url is not properly formatted or that the server is not parsing it correctly. (That is, the key-value pairs appear to be properly making it from the command line to the function that contains the request.)
import sys
import requests
server_url = "http://0.0.0.0:5006"
def test(payload):
print('payload in function is ' + payload)
r = requests.get('http://0.0.0.0:5006/buy', params=payload)
print(r.url) #debug
print(r.encoding)
print(r.text)
print(r.headers)
if __name__ == '__main__':
payload = sys.argv[2]
print('payload from cli is ' + payload)
test(payload)
Server:
import subprocess
from flask import Flask
from flask import request
import request
# Configure the app and wallet
app = Flask(__name__)
#app.route('/buy', methods=['GET', 'POST'])
def test(key1, key2):
key1 = str(request.args.get('key1'))
key2 = str(request.args.get('key2'))
print('keys are' + key1 + key2)
fortune = subprocess.check_output(['echo', 'key1'])
return fortune
# Initialize and run the server
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5006)
Client console output:
payload from cli is {"key1": "foo", "key2": "bar"}
payload in function is {"key1": "foo", "key2": "bar"}
http://0.0.0.0:5006/buy?%7B%22key1%22:%20%22foo%22,%20%22key2%22:%20%22bar%22%7D
ISO-8859-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
{'Content-Type': 'text/html', 'Content-Length': '291', 'Date': 'Fri, 09 Sep 2016 22:16:44 GMT', 'Server': 'Werkzeug/0.11.10 Python/3.5.2'}
Server console output:
* Running on http://0.0.0.0:5006/ (Press CTRL+C to quit)
[2016-09-09 18:30:33,445] ERROR in app: Exception on /buy [GET]
Traceback (most recent call last):
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1988, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1641, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1544, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.5/dist-packages/flask/_compat.py", line 33, in reraise
raise value
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1639, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1625, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
TypeError: test() missing 2 required positional arguments: 'key1' and 'key2'
127.0.0.1 - - [09/Sep/2016 18:30:33] "GET /buy?%7B%22key1%22:%20%22foo%22,%20%22key2%22:%20%22bar%22%7D HTTP/1.1" 500 -
I take this as showing that the subprocess call is for some reason not able to decipher key1 and key2 from the URL so is failing when it runs "fortune None None".
The problem seems to be in your payload.
The payload needs to be a dictionary. You're giving it a string.
sys.argv[2] will be a string, even if you format the text to look like a dictionary. So unless there's something missing from your client code snippet, payload isn't actually a dictionary like requests would expect.
I can infact confirm this by looking at the URL being generated, which is:
http://0.0.0.0:5006/buy?%7B%22key1%22:%20%22foo%22,%20%22key2%22:%20%22bar%22%7D
Had the payload been a dictionary and correctly encoded, it would've looked something like this:
http://0.0.0.0:5006/buy?key1=foo&key2=bar
To see what type payload your really is, do print(type(payload)) somewhere inside the test function.
Once you've converted your payload into a proper python dictionary (you'll have to parse your sys.argv[2]), then requests should work as expected.
test is expecting values for key1 and key2. Flask would provide those through your route.
#app.route('/buy/<key1>/<key2>')
def test(key1, key2):
Visiting /buy/value1/value2 would give values to the arguments. You want to pass values through the query string though.
You just need to remove them from the function's signature.
#app.route('/buy', methods=['GET', 'POST'])
def test():
Flask code -
#app.route('/messages', methods = ['POST'])
def api_message():
if request.headers['Content-Type'] == 'text/plain':
return "Text Message: " + request.data
elif request.headers['Content-Type'] == 'application/json':
f = open(filename,'r')
l = f.readlines()
f.close()
return len(l)
On running, I get error as -
curl -H "Content-Type:application/json" -X POST http://127.0.0.1:5000/messages --data filename=#hello.json
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
Am I accessing the curl param wrong (filename)? Or I am sending the file in wrong way?
Also Upload a file to a python flask server using curl
Tried doing
f = request.files['filename']
Still, same error.
What your curl command code is doing is reading the file hello.json and putting it in the body of the request. (This feature is actually very useful if you have a large chunk of JSON you need to send to the server).
Normally in application/json requests you send the JSON as the body of the request, so this may be what you want. You can use request.get_json to get this data as a Python dictionary.
If you want to upload an actual file - like uploading a picture - you want multi part form encoding, which you tell curl to send via the -F parameter. (See also: an SO answer about this: https://stackoverflow.com/a/12667839/224334 ).
Apologies if this seems rudimental as I am new to Python. The task I am trying to complete is to send a json object from an iPhone app to a python script that will process a stripe payment. The problem I have is I cannot figure out how to get Python to recognise the incoming json object to extract data from it and pass onto Stripe.
I have taken a step back to simplify the problem. I have a python script that attempts to post a json object with four value pairs to another function that should extract the values, create a new json object and return that object. I cannot get it to work and any help would be greatly appreciated as I've been stuck on this for a while. I am using Flask:
`
import json
import stripe
import smtplib
import requests
from flask import Flask, request, jsonify
#application.route('/run_post')
def run_post():
url = 'http://xxx.pythonanywhere.com/stripetest'
data = {'stripeAmount': '199', 'stripeCurrency': 'USD', 'stripeToken': '122', 'stripeDescription': 'Test post'}
headers = {'Content-Type' : 'application/json'}
r = requests.post(url, data, headers=headers)
#return json.dumps(r.json(), indent=4)
return r.text
#application.route('/stripetest', methods=["POST"])
def stripeTest():
if request.method == "POST":
json_dict = json.loads(request.body.raw)
stripeAmount = json_dict['stripeAmount']
stripeCurrency = json_dict['stripeCurrency']
stripeToken = json_dict['stripeToken']
stripeDescription = json_dict['stripeDescription']
data = "{'stripeAmountRet': " + stripeAmount + ", 'stripeCurrencyRet': " + stripeCurrency + ", 'stripeTokenRet': " + stripeToken + ", 'stripeDescriptionRet': " + stripeDescription + "}"
return jsonify(data)
else:
return """<html><body>
Something went horribly wrong
</body></html>"""
`
I get the following returned in the error log when I run this:
`
2015-03-19 21:07:47,148 :Starting new HTTP connection (1): xxx.pythonanywhere.com
2015-03-19 21:07:47,151 :Exception on /stripetest [POST]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1687, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1360, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1358, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1344, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/var/www/xxx_pythonanywhere_com_wsgi.py", line 156, in stripeTest
json_dict = json.loads(request.body.raw)
File "/usr/local/lib/python2.7/dist-packages/werkzeug/local.py", line 336, in __getattr__
return getattr(self._get_current_object(), name)
AttributeError: 'Request' object has no attribute 'body'
`
You have a couple of issues with the code. First of all, you need to properly define the json data when you make the request from the requests library. You can do this as follows:
#application.route('/run_post')
def run_post():
url = 'http://xxx.pythonanywhere.com/stripetest'
data = {'stripeAmount': '199', 'stripeCurrency': 'USD', 'stripeToken': '122', 'stripeDescription': 'Test post'}
headers = {'Content-Type' : 'application/json'}
r = requests.post(url, data=json.dumps(data), headers=headers)
#return json.dumps(r.json(), indent=4)
return r.text
Notice that we call json.dumps instead of just passing the data directly. Otherwise, the incoming request is not interpreted as json data.
Next, in your receiving function, we change it as follows:
#application.route('/stripetest', methods=["POST"])
def stripeTest():
if request.method == "POST":
json_dict = request.get_json()
stripeAmount = json_dict['stripeAmount']
stripeCurrency = json_dict['stripeCurrency']
stripeToken = json_dict['stripeToken']
stripeDescription = json_dict['stripeDescription']
data = {'stripeAmountRet': stripeAmount, 'stripeCurrencyRet': stripeCurrency, 'stripeTokenRet': stripeToken, 'stripeDescriptionRet': stripeDescription}
return jsonify(data)
else:
return """<html><body>
Something went horribly wrong
</body></html>"""
A couple of things are changed. First, we read in the data by calling request.get_json(), which properly parses the incoming json data. Note from above that we needed to change how we actually made the request for it to parse the data properly. The next issue was how you returned the data. To properly jsonify the data to return, we put the data into a python dictionary rather than into a string.
If you're calling your function to process the stripe payment from somewhere else (i.e. not using the python requests library), another issue is that you may not be defining the json request properly for Flask to later interpret. If the issue still persists after making the above change to the processing function, post how you're making the json request elsewhere and I can take a look.
Let me know if this fixes your problems!
You should check the document Flask requests
It does not define a body, instead you should try with
request.get_json()
You just need to make sure you are specifying the correct mimetype which would be "application/json".
See request.get_json() method for more info
I'm having a difficulty trying to make a Python REST POST to a webservice running on Glassfish. I have verified that POST works ok using CURL but having no luck with Python.
Here is the CURL request that works ok.
curl -X POST -H "Content-Type: application/json" -d '{"id":1,"lastname":"smith"}'
http://192.168.0.20:8080/field1/resources/com.field1entity.field1
Here is the Python code to make the POST request
import urllib
import httplib2
def call():
http = httplib2.Http()
url = 'http://192.168.0.20:8080/field1/resources/com.field1entity.field1'
params = urllib.urlencode({"id":11111,"lastname":"oojamalip"})
response, content = http.request(url, 'POST', params, headers={'Content-type':'application/json'})
print "lets stop here to have a looksy at the variables"
print content
if __name__ == '__main__':
namesPage = call()
print namesPage
Output from console,
Unexpected character ('l' (code 108)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: org.apache.catalina.connector.CoyoteInputStream#18f494d; line: 1, column: 2]
Hope someone can shed some light on the problem.
thanks
Nick
You are url encoding the prams and then telling the server it is json encoded
import json
params = json.dumps({"id":11111,"lastname":"oojamalip"})
# then
response, content = http.request(url, 'POST', body=params, headers={'Content-type':'application/json'})