Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In a Python3 CGI script, how can I read the raw form data in a POST?

Yes, I'm aware of cgi.FieldStorage, but, being some form of unordered dictionary, it does not preserve the order of the original data (see below for proof). Since I wish to use this data with PayPal IPN, order is important PayPal docs here, which say "...you must send back the contents in the exact order they were received..."

Alternatively, os.environ.get('QUERY_STRING') looks perfect, however, it seems to only work for a 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 a GET from the browser, e.g. (note that foo is 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 returns no query string, and, as expected, the FormData did not preserve the order.

query=no query
ggg=3
foo=bar   << note that foo now comes after ggg
aaa=bbb
zzz=qqq
like image 366
user949300 Avatar asked Dec 18 '14 22:12

user949300


1 Answers

Not sure why nobody answered. After a little futzing I discovered that the solution is incredibly simple. Is that why nobody bothered to answer?

Just read from stdin: (this is Python3 code, not sure if Python2 would be any different)

query_string = sys.stdin.read()

There's one drawback: this is incompatible with cgi.FieldStorage(), since that will also try to read from stdin. So, if you also want a nice dictionary to look up query terms, there is one more simple step:

multiform = urllib.parse.parse_qs(query_string)

which, much like cgi.FieldStorage, returns a multimap, hence the name multiform.

For some more details, I blogged about it here.

like image 193
user949300 Avatar answered Nov 13 '22 17:11

user949300