In a CGI Python3 script, how can I read raw form data in POST?
Yes, I know cgi.FieldStorage
, but being some form of an unordered dictionary, it doesn't preserve the order of the original data (see below for proof). Since I want to use this data in the PayPal IPN, it is important to include "PayPal Documents" which says "... you must send back the content in the exact order you received it ..."
Alternatively os.environ.get('QUERY_STRING')
looks perfect, however it only works for GET . Example code: (myscript.py)
#!/usr/bin/python3
import cgi, os
query = os.environ.get('QUERY_STRING') or 'no query'
print ("Content-type: text/plain\n\n")
print("query=" + query)
form = cgi.FieldStorage()
for key in form.keys():
print("\n" + key + '=' + form.getvalue(key))
Works with GET from the browser, for example. (note that foo comes before ggg)
http://example.com/myscript.py/foo=bar&ggg=3&aaa=bbb&zzz=qqq
returns
query=foo=bar&ggg=3&aaa=bbb&zzz=qqq
ggg=3
foo=bar << note that foo now comes after ggg
aaa=bbb
zzz=qqq
However, if I use Postman to POST
POST /myscript.py HTTP/1.1
Host: example.com
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
foo=bar&ggg=3&aaa=bbb&zzz=qqq
It does not return a query string, and as expected, FormData did not preserve order.
query=no query
ggg=3
foo=bar << note that foo now comes after ggg
aaa=bbb
zzz=qqq
source to share
I don't know why no one answered. After a little futzing, I found the solution to be incredibly simple . Is that why no one bothered to answer?
Just read from stdin: (this is Python3 code, not sure if Python2 will be different)
query_string = sys.stdin.read()
There is one drawback: it is incompatible with cgi.FieldStorage()
, since this will also try to read from stdin. So, if you also want a good dictionary to look up query terms, there is one more simple step:
multiform = urllib.parse.parse_qs(query_string)
which, like cgi.FieldStorage, returns multimap , so the name is multiple.
For more details I wrote about it here .
source to share