Python’s core cgi module is a great helper for slim rest apis.
Unfortunately field parsing with cgi.FieldStorage()
does not support JSON data requests and will raise a TypeError.
request
curl \
--request POST \
--header "Content-Type: application/json" \
--header "X-Marvin-Status: depressed" \
--data '{"name": "Deep Thought", "answer": 42}' \
http://raspberrypi.local:9000/hitchhiker/api/ultimate-question.py?test=1
server
#!/usr/bin/env python3
import cgi
params = cgi.FieldStorage()
print(params)
#> ...TypeError: write() argument must be str, not bytes
Solution 1: convert your request to an urlencoded form request
request
curl \
--request POST \
--header "Content-Type: application/x-www-form-urlencoded" \
--header "X-Marvin-Status: depressed" \
--data 'name=Deep Thought&answer=42' \
http://raspberrypi.local:9000/hitchhiker/api/ultimate-question.py?test=1
server
#!/usr/bin/env python3
import cgi
params = cgi.FieldStorage()
print(params)
#> FieldStorage(None, None, [MiniFieldStorage('name', 'Deep Thought'), MiniFieldStorage('answer', '42'), MiniFieldStorage('test', '1')])
The clear disadvantage is here that you have to typecast your data properties twice.
- on client, when encoding the data
- on server, when processing the params
Solution 2: parse request manually
Read the raw request body directly and parse query with urllib
request
curl \
--request POST \
--header "Content-Type: application/json" \
--header "X-Marvin-Status: depressed" \
--data '{"name": "Deep Thought", "answer": 42}' \
http://raspberrypi.local:9000/hitchhiker/api/ultimate-question.py?test=1
server
#!/usr/bin/env python3
import os
import sys
import json
from urllib.parse import parse_qs
content_len = os.environ.get('CONTENT_LENGTH', '0')
method = os.environ.get('REQUEST_METHOD', '')
query_string = os.environ.get('QUERY_STRING', '')
x_header = os.environ.get('HTTP_X_MARVIN_STATUS', '')
body = sys.stdin.read(int(content_len))
res = json.loads(body)
print('method: ', method)
print('header[X-Marvin-Status]: ', x_header)
print('query: ', query_string)
print('json: ', res)
if not query_string:
exit()
query = parse_qs(query_string)
print('test: ', query['test'][0])
#> method: POST
#> header[X-Marvin-Status]: depressed
#> query: test=1
#> json: {'username': 'xyz', 'password': 42}
#> test: 1