Python-flask extracting values from a POST request to REST API - python

I am trying to extract values from a JSON payload by POST request:
Question 1:
What is a better way to get the data?
First Way?
#app.route('/login', methods=['POST'])
def login():
if request.method == 'POST':
test = request.data
return test
or Second Way?
#app.route('/login', methods=['POST'])
def login():
if request.method == 'POST':
test = json.loads(request.json)
return test
Question 2:
How do I get a specific value?
Given the json payload is:
{ "test": "hello world" }
I tried doing code below, but does not work.
#way1
return request.data["test"]
#error message: TypeError: string indices must be integers, not str
#way2
test["test"]
#error message: TypeError: expected string or buffer

If you are posting JSON to a flask endpoint you should use:
request.get_json()
A very important API note:
By default this function will return None if the mimetype is not application/json but this can be overridden by the force parameter
Meaning... either make sure the mimetype of the POST is application/json OR be sure to set the force option to true
A good design would put this behind:
request.is_json like this:
#app.route('/post/', methods=['POST'])
def post():
if request.is_json:
data = request.get_json()
return jsonify(data)
else:
return jsonify(status="Request was not JSON")

Related

Request body returning None in google function [duplicate]

I'm trying to build a simple API using Flask, in which I now want to read some POSTed JSON. I do the POST with the Postman Chrome extension, and the JSON I POST is simply {"text":"lalala"}. I try to read the JSON using the following method:
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.json
print content
return uuid
On the browser it correctly returns the UUID I put in the GET, but on the console, it just prints out None (where I expect it to print out the {"text":"lalala"}. Does anybody know how I can get the posted JSON from within the Flask method?
First of all, the .json attribute is a property that delegates to the request.get_json() method, which documents why you see None here.
You need to set the request content type to application/json for the .json property and .get_json() method (with no arguments) to work as either will produce None otherwise. See the Flask Request documentation:
This will contain the parsed JSON data if the mimetype indicates JSON (application/json, see is_json()), otherwise it will be None.
You can tell request.get_json() to skip the content type requirement by passing it the force=True keyword argument.
Note that if an exception is raised at this point (possibly resulting in a 400 Bad Request response), your JSON data is invalid. It is in some way malformed; you may want to check it with a JSON validator.
For reference, here's complete code for how to send json from a Python client:
import requests
res = requests.post('http://localhost:5000/api/add_message/1234', json={"mytext":"lalala"})
if res.ok:
print(res.json())
The "json=" input will automatically set the content-type, as discussed here: How to POST JSON data with Python Requests?
And the above client will work with this server-side code:
from flask import Flask, request, jsonify
app = Flask(__name__)
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.json
print(content['mytext'])
return jsonify({"uuid":uuid})
if __name__ == '__main__':
app.run(host= '0.0.0.0',debug=True)
This is the way I would do it and it should be
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.get_json(silent=True)
# print(content) # Do your processing
return uuid
With silent=True set, the get_json function will fail silently when trying to retrieve the json body. By default this is set to False. If you are always expecting a json body (not optionally), leave it as silent=False.
Setting force=True will ignore the
request.headers.get('Content-Type') == 'application/json' check that flask does for you. By default this is also set to False.
See flask documentation.
I would strongly recommend leaving force=False and make the client send the Content-Type header to make it more explicit.
Assuming you've posted valid JSON with the application/json content type, request.json will have the parsed JSON data.
from flask import Flask, request, jsonify
app = Flask(__name__)
#app.route('/echo', methods=['POST'])
def hello():
return jsonify(request.json)
For all those whose issue was from the ajax call, here is a full example :
Ajax call : the key here is to use a dict and then JSON.stringify
var dict = {username : "username" , password:"password"};
$.ajax({
type: "POST",
url: "http://127.0.0.1:5000/", //localhost Flask
data : JSON.stringify(dict),
contentType: "application/json",
});
And on server side :
from flask import Flask
from flask import request
import json
app = Flask(__name__)
#app.route("/", methods = ['POST'])
def hello():
print(request.get_json())
return json.dumps({'success':True}), 200, {'ContentType':'application/json'}
if __name__ == "__main__":
app.run()
If you use force=True, it will ignore the content type of the request and try to parse the body as JSON regardless.
request.get_json(force=True)
You may note that request.json or request.get_json() works only when the Content-type: application/json has been added in the header of the request. If you are unable to change the client request configuration, so you can get the body as json like this:
data = json.loads(request.data)
To give another approach.
from flask import Flask, jsonify, request
app = Flask(__name__)
#app.route('/service', methods=['POST'])
def service():
data = json.loads(request.data)
text = data.get("text",None)
if text is None:
return jsonify({"message":"text not found"})
else:
return jsonify(data)
if __name__ == '__main__':
app.run(host= '0.0.0.0',debug=True)
The following codes can be used:
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.json['text']
print content
return uuid
Here is a screenshot of me getting the json data:
You can see that what is returned is a dictionary type of data.
Assuming that you have posted valid JSON,
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.json
print content['uuid']
# Return data as JSON
return jsonify(content)
Even though all the answers I encounter here are right. There is something that I think it should be done as better practice. Here is how I would write it.
from flask import app, request, Flask, jsonify
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
# Check if the request method is POST
if request.method == 'POST':
# content will return eather parse data as JSON
# Or None incase there is no data
content = request.get_json()
print(content)
# The content could be displayed in html page if serialized as json
return jsonify(content) # Return null if there is content
# if it is only get request then just return uuid
return uuid
{
"uuid":1212121212,
"text":"lalala",
"comment":"",
"signed_on":"2022-11-07 00:03:00"
}
you can sent the above data as json and get it in flask application using request.json
from flask import request, Blueprint,flash,json
#app.route('/api/add_message/<uuid>', methods = ["GET", "POST"])
def testing(uuid):
sync_data = request.json
josn_data = json.dumps(sync_data ,default =str)
return josn_data
Try to set force attribute as True in get_json() method to resolve this issue.
request.get_json(force = True)

flask url function not working properly

I have a an html form that is sending data with POST then I redirect to the same function using GET with the form data as arguments for the function
#app.route('/search/custom/', methods=['GET', 'POST'])
def search_custom(category=None, date=None, page=None):
if request.method == 'POST':
category = request.form.get('InputCategory')
date = request.form.get('InputDate')
return redirect(url_for('search_custom', category=category, date=date, page=1))
if request.method == 'GET':
if not(category and date and page):
return redirect(url_for('home'))
flash('worked', 'success')
return redirect(url_for('register'))
function is receiving the arguments correctly yet its only redirecting to "home":
127.0.0.1 - - [10/Apr/2018 01:55:36] "GET /search/custom/?category=Vetements&date=Dernier+mois&page=1 HTTP/1.1" 302 -
category, date, and page are all None when processing a get request.
Inside of the Get handler, you'll need to actually pull the parameters from the query string.
Something like:
category = request.args.get('category')
date = request.args.get('date')
page = request.args.get('page')
should do the trick.
Put the before the check for the parameters and it should work.
I didn't have a chance to test this, so let me know if it doesn't work and I'll actually dig into it a bit more.
The way you have it now, you would probably want to have a formatted url for. Something like /search/custom/<category>/<date>/<page>. This would require changing the format of the incoming url too though, and that probably isn't what you want.
The code would look something like
#app.route('/search/custom/<category>/<date>/<page>', methods=['GET', 'POST'])
#app.route('/search/custom/', methods=['GET', 'POST'])
def search_custom(category=None, date=None, page=None):
# do stuff

Print JSON Data in output within FLASK [duplicate]

This question already has an answer here:
Print JSON Data - Flask [closed]
(1 answer)
Closed 6 years ago.
So I have this test app (learning flask). It takes a username and a message input and displays it on the third page. The last "return" below spits out that data. I need to include the JSON equivalent of the data below it. I think I need to put 'username' and 'message' into a variable and call json dumps to return it also? killing myself over here.
from flask import Flask, render_template, request,
redirect, url_for,abort, session
import json
from json import dumps
app = Flask(__name__)
app.config['SECRET_KEY'] = 'F34TF$($e34D';
#app.route('/')
def home():
return render_template('index.html')
#app.route('/signup', methods=['POST'])
def signup():
session['username'] = request.form['username']
session['message'] = request.form['message']
return redirect(url_for('message'))
#app.route('/message')
def message():
if not 'username' in session:
return abort(403)
return render_template('message.html',
username=session['username'],message=session['message']),
if __name__ == '__main__':
app.run()
This is what you need. The page also has example. You can do something like below. Normally you would want to respond with either a html or json depending on the accept header. It defines what data the client is expecting.
If accept is 'application/json' use jsonify. if accept is 'text/html', then render your template. That way your page will be normal when loaded from browser, however when using a restclient with accept headers set appropriately, you get json responses.
from flask import request
#app.route('/message')
def message():
if not 'username' in session:
return abort(403)
if request.headers['accept'] == 'text/html':
return render_template('message.html',
username=session['username'],message=session['message'])
elif request.headers['accept'] == 'application/json':
return jsonify(
username=session['username'], message=session['message'])

Unable to read POSTed application/json data in Flask

I have a flask app which has one endpoint as:
#app.route('/restaurant/<restaurant_id>', methods=['GET', 'POST'])
def detail(restaurant_id=None):
if request.method == 'GET':
#something
elif request.method == 'POST':
print jsonify(request.get_json(force=True)) #This doesn't work if I set content type as applicaiton/json
comments = [request.form['comment']] #this works if I set content type as form-data
# something
return Response(json.dumps({
'success': True,
'response': json_docs
}), status=200, content_type='application/json')
For some reason, I'm unable to read the POSTed data when it is posted as a JSON with application/json as the Content-Type. The print command just returns None. How do I read the Json data?
request.get_json() returns JSON, so jsonify should turn it back into JSON. And comments should be request.get_json(force=True). Hope this helps.

How to get POSTed JSON in Flask?

I'm trying to build a simple API using Flask, in which I now want to read some POSTed JSON. I do the POST with the Postman Chrome extension, and the JSON I POST is simply {"text":"lalala"}. I try to read the JSON using the following method:
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.json
print content
return uuid
On the browser it correctly returns the UUID I put in the GET, but on the console, it just prints out None (where I expect it to print out the {"text":"lalala"}. Does anybody know how I can get the posted JSON from within the Flask method?
First of all, the .json attribute is a property that delegates to the request.get_json() method, which documents why you see None here.
You need to set the request content type to application/json for the .json property and .get_json() method (with no arguments) to work as either will produce None otherwise. See the Flask Request documentation:
This will contain the parsed JSON data if the mimetype indicates JSON (application/json, see is_json()), otherwise it will be None.
You can tell request.get_json() to skip the content type requirement by passing it the force=True keyword argument.
Note that if an exception is raised at this point (possibly resulting in a 400 Bad Request response), your JSON data is invalid. It is in some way malformed; you may want to check it with a JSON validator.
For reference, here's complete code for how to send json from a Python client:
import requests
res = requests.post('http://localhost:5000/api/add_message/1234', json={"mytext":"lalala"})
if res.ok:
print(res.json())
The "json=" input will automatically set the content-type, as discussed here: How to POST JSON data with Python Requests?
And the above client will work with this server-side code:
from flask import Flask, request, jsonify
app = Flask(__name__)
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.json
print(content['mytext'])
return jsonify({"uuid":uuid})
if __name__ == '__main__':
app.run(host= '0.0.0.0',debug=True)
This is the way I would do it and it should be
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.get_json(silent=True)
# print(content) # Do your processing
return uuid
With silent=True set, the get_json function will fail silently when trying to retrieve the json body. By default this is set to False. If you are always expecting a json body (not optionally), leave it as silent=False.
Setting force=True will ignore the
request.headers.get('Content-Type') == 'application/json' check that flask does for you. By default this is also set to False.
See flask documentation.
I would strongly recommend leaving force=False and make the client send the Content-Type header to make it more explicit.
Assuming you've posted valid JSON with the application/json content type, request.json will have the parsed JSON data.
from flask import Flask, request, jsonify
app = Flask(__name__)
#app.route('/echo', methods=['POST'])
def hello():
return jsonify(request.json)
For all those whose issue was from the ajax call, here is a full example :
Ajax call : the key here is to use a dict and then JSON.stringify
var dict = {username : "username" , password:"password"};
$.ajax({
type: "POST",
url: "http://127.0.0.1:5000/", //localhost Flask
data : JSON.stringify(dict),
contentType: "application/json",
});
And on server side :
from flask import Flask
from flask import request
import json
app = Flask(__name__)
#app.route("/", methods = ['POST'])
def hello():
print(request.get_json())
return json.dumps({'success':True}), 200, {'ContentType':'application/json'}
if __name__ == "__main__":
app.run()
You may note that request.json or request.get_json() works only when the Content-type: application/json has been added in the header of the request. If you are unable to change the client request configuration, so you can get the body as json like this:
data = json.loads(request.data)
If you use force=True, it will ignore the content type of the request and try to parse the body as JSON regardless.
request.get_json(force=True)
To give another approach.
from flask import Flask, jsonify, request
app = Flask(__name__)
#app.route('/service', methods=['POST'])
def service():
data = json.loads(request.data)
text = data.get("text",None)
if text is None:
return jsonify({"message":"text not found"})
else:
return jsonify(data)
if __name__ == '__main__':
app.run(host= '0.0.0.0',debug=True)
The following codes can be used:
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.json['text']
print content
return uuid
Here is a screenshot of me getting the json data:
You can see that what is returned is a dictionary type of data.
Assuming that you have posted valid JSON,
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
content = request.json
print content['uuid']
# Return data as JSON
return jsonify(content)
Even though all the answers I encounter here are right. There is something that I think it should be done as better practice. Here is how I would write it.
from flask import app, request, Flask, jsonify
#app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
# Check if the request method is POST
if request.method == 'POST':
# content will return eather parse data as JSON
# Or None incase there is no data
content = request.get_json()
print(content)
# The content could be displayed in html page if serialized as json
return jsonify(content) # Return null if there is content
# if it is only get request then just return uuid
return uuid
{
"uuid":1212121212,
"text":"lalala",
"comment":"",
"signed_on":"2022-11-07 00:03:00"
}
you can sent the above data as json and get it in flask application using request.json
from flask import request, Blueprint,flash,json
#app.route('/api/add_message/<uuid>', methods = ["GET", "POST"])
def testing(uuid):
sync_data = request.json
josn_data = json.dumps(sync_data ,default =str)
return josn_data
Try to set force attribute as True in get_json() method to resolve this issue.
request.get_json(force = True)

Categories