My understanding is that request.args in Flask contains the URL encoded parameters from a GET request while request.form contains POST data. What I'm having a hard time grasping is why when sending a POST request, trying to access the data with request.form returns a 400 error but when I try to access it with request.args it seems to work fine.
I have tried sending the request with both Postman and curl and the results are identical.
curl -X POST -d {"name":"Joe"} http://127.0.0.1:8080/testpoint --header "Content-Type:application/json"
Code:
#app.route('/testpoint', methods = ['POST'])
def testpoint():
name = request.args.get('name', '')
return jsonify(name = name)
You are POST-ing JSON, neither request.args nor request.form will work.
request.form works only if you POST data with the right content types; form data is either POSTed with the application/x-www-form-urlencoded or multipart/form-data encodings.
When you use application/json, you are no longer POSTing form data. Use request.get_json() to access JSON POST data instead:
#app.route('/testpoint', methods = ['POST'])
def testpoint():
name = request.get_json().get('name', '')
return jsonify(name = name)
As you state, request.args only ever contains values included in the request query string, the optional part of a URL after the ? question mark. Since it’s part of the URL, it is independent from the POST request body.
Your json data in curl is wrong, so Flask does not parse data to form.
Send data like this: '{"name":"Joe"}'
curl -X POST -d '{"name":"Joe"}' http://example.com:8080/testpoint --header "Content-Type:application/json"
just change args for form and it will work
#app.route('/testpoint', methods = ['POST'])
def testpoint():
name = request.form.get('name', '')`enter code here`
return jsonify(name = name)
Related
I am trying to write a flask application that lets users select some options on a page, creates json string from them, and passes it to another 'build()' function, which is a flask route.
I should also be able to POST a json directly to build().
To avoid code duplication, I tried redirecting the 'Build' button in my form using code 307 and a json argument.
But I want to be able to pass it just as a curl request would do it with json content-type (option #1 below), so I can access the passed json using requests['json'], instead of requests.args['input']
def gui():
...
elif request.form['submit'] == 'Build':
return redirect(url_for('build', json=session['json']), code=307)
...
def build():
input = request.json #1. curl -H "Content-Type: application/json" --data #input.json http://localhost/build
input = json.loads(request.args['input']) #2. curl -H http://localhost/build?input=%7B"database"%3A...
...
How do I set my redirect, response, header etc. in the gui() 'Build' section ?
I'm trying to make a put request to the local server using the put request using curl:
curl -X PUT -H "Content-Type: application/json" -d '{"connid":"12"}' "127.0.0.1:8000/api/kill"
I receive the same response:
'WSGIRequest' object has no attribute 'PUT'
for the following code:
def kill(req):
conid = req.PUT['connid']
statusres = {}
if conid in state:
error[conid] = 'true'
statusres['status'] = 'ok'
else:
statusres['status'] = 'invalid connection Id : '+ conid
return JsonResponse(statusres)
I also used #csrf_exempt before the function.
You have misunderstood several things here.
When you send form-encoded data, whether it's POST or PUT, in Django you always find the parameters in request.POST. So you would find your data in request.POST['conid'].
However, you are not sending form-encoded data; you are sending JSON. You need to access the request body, and pass it to the json.loads function to decode:
def kill(request):
data = json.loads(request.body)
conid = data['connid']
I'm using FLASK API and I want to use POST requests.
I want just to do an example with POST requests that will return something, I keep getting an error message "Method Not Allowed".
I want to give a parameter(e.g query_params = 'name1' ) to search for a user and to return a JSON, actually I don't know where to give this parameter and I don't understand why I'm getting that message.
Here I did a simple route:
#mod_api.route('/show-user', methods=['POST'])
def show_user():
query_params = 'name1'
query = {query_params: 'Myname' }
json_resp = mongo.db.coordinates.find(query)
return Response(response=json_util.dumps(json_resp), status=200, mimetype='application/json')
Any help please?
The likely reason is that you are probably not doing a POST request against the route, which only accepts POST requests. Here is a simplified example with the mongodb details removed to illustrate this.
from flask import Flask
app = Flask(__name__)
#app.route('/show-user', methods=('POST',))
def show_user():
return "name info"
if __name__ == "__main__":
app.run(debug=True)
Now if we do a POST request it works, but if we do A GET request it raises the error you saw:
curl -H "Content-Type: application/json" -X POST -d '{}' http://127.0.0.1:5000/show-user
name info
curl -H "Content-Type: application/json" -X GET http://127.0.0.1:5000/show-user
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>
For example I tried that command via terminal:
curl -F "profileImage=#/home/user/image.jpg" -F "{'firstName':'hello'};type=application/json" http://127.0.0.1:8000/api/v1/signup/
Then I received the request object like that:
print request.FILES
# <MultiValueDict: {u'profileImage': [<InMemoryUploadedFile: image.jpg (image/jpeg)>]}>
print request.DATA
# <QueryDict: {u"{'firstName':'hello'};content-type": [u'application/json']}>
The image is ok, but QueryDict is not represented correctly - all JSON file is a key and content-type
is a value.
In Django use these parsers:
parser_classes = (MultiPartParser, FormParser, JSONParser,)
I necessary need to send text data via JSON structure .
If you want to POST with multipart/form-data content-type, you can't also specify application/json. The simple fix for this is to send the form data in url-encoded format. Try this command:
curl -F "profileImage=#/home/user/image.jpg" -F "firstName=hello" http://127.0.0.1:8000/api/v1/signup/
I came up with a solution like this:
class MultiPartJSONParser(parsers.MultiPartParser):
def parse(self, stream, media_type=None, parser_context=None):
dataAndFiles = super(MultiPartJSONParser, self).parse(stream, media_type, parser_context)
try:
jsonData = json.loads(dataAndFiles.data.get('data'))
except ValueError as exc:
raise parsers.ParseError('JSON parse error - %s' % six.text_type(exc))
# make data mutable, insert json data, and remove raw data
dataAndFiles.data = data = dataAndFiles.data.copy()
data.update(jsonData)
del data['data']
return dataAndFiles
It assumes that the JSON part is sent as a form field called 'data'.
If you want to get really slick, you could probably parse the data field according to the media type value of the Accept header. Alternatively, you could POST the data as a file and parse according to its content type.
Note that to use this parser class, you'll need to set the following default settings:
REST_FRAMEWORK = {
'FORM_METHOD_OVERRIDE': None,
'FORM_CONTENT_OVERRIDE': None,
}
That is due to this bug: https://github.com/tomchristie/django-rest-framework/issues/1346
the parameter to specify the content-type is just "type="
curl -F "profileImage=#/home/user/image.jpg" -F "{'firstName':'hello'};type=application/json" http://127.0.0.1:8000/api/v1/signup/
However, I don't think that will allow the JSONParser to take the information..., but you can try :)
Here :-
curl -vvv -X POST -H "Content-Type:multipart/form-data" -H "Accept:application/json" -H -F "username=sample" -F "password=something" -F "image=#Mercury.gif" http://127.0.0.1:8000/api/objects
No need to type accepts application/json. Django will automatically treat these as dictionary objects.
Print request.DATA and request.FILES will give you
<QueryDict: {u'username': [u'sample'] , u'password': [u'something']}>
<MultiValueDict: {u'image': [<InMemoryUploadedFile: Mercury.gif (image/gif)>]}>
Thanks xjtian - link that you pasted in comment + some researches make solution.
So:
If you want sending json data + file - send two requests. First will create object on backend and give id to app. Second will update object with file using gived id. More info there.
If you don't care about data types - send request via "multipart/form-data".
Thanks to all for answers, you realy helps, but I think this one is more appropriate for REST architecture.
I am working on a Django server that takes an integer from POST data. If I send the integer via GET there's no problems, but it gets dropped when sent via POST.
I run the server with:
./manage.py runserver 0.0.0.0:8000
and then generate the POST request with:
curl -X POST -H "Content-Type: application/json" -d '{"myInt":4}' "http://0.0.0.0:8000/myURL/"
I am using PDB in the view, and here is the result I am getting for the following commands:
request.method
> 'POST'
request.body
> '{"myInt":4}'
request.POST
> <QueryDict: {}>
I have used #csrf_exempt as a decorator for the view, just to make sure that isn't causing any problems.
Currently it is baffling me that request.POST does not contain myInt, is my POST request not well-formed?
Your post request is sending JSON not application/x-www-form-urlencoded
If you look at the django docs about HttpRequest.POST .
A dictionary-like object containing all given HTTP POST parameters, providing that the request contains form data. See the QueryDict documentation below. If you need to access raw or non-form data posted in the request, access this through the HttpRequest.body attribute instead.
You need to POST form encoded data. I didn't test this and haven't used curl in a while, but I believe the following should work.
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "myInt=4" "http://0.0.0.0:8000/myURL/"