Reputation: 47
I've been trying to build an API for more than 4 hours now and I searched and asked everywhere I could but I can't find help. The problem is at the level of handling POST requests. I tried with NodeJS (testify and express (as well as middlewares)) and Python (Flask, bottle) and I still can't get why I get an empty object with express or None in python. I have the following code with bottle
1 from bottle import run, Bottle, post, request, redirect
2 import json
3
4 pika = Bottle()
5
6 @post("/shorturl")
7 def shorten():
8 data = request.json
9 #data = json.dumps(rdata)
10 print(data)
11 return f"You posted {data}"
12
13 run(host="localhost", port=3000, debug=True)
And I had the following code at the beginning (I deleted and restarted from scratch) - you can find tweet here.
I can't get None with flask and bottle when using request.get_json()
and request.json()
respectively which I've found are the way to do it from the docs.
Any help appreciated.
Thanks
Upvotes: 0
Views: 1298
Reputation: 3907
Here is a way to handle a dynamic routing of the api. Now you just have to add methods to the API class and they are automatically picked up by the bottle app. I am merging POST and GET into one method string to merge query parameters and forms into one payload which you can access via self.payload
import ujson as json
from login import User
def check(data):
try:
if isinstance(data, (str,bytes)):
return json.loads(data)
except:
return data
return data
def merge_dicts(*args):
result = {}
for dictionary in args:
result.update(dictionary or {})
return result
class API:
def __init__(self, payload, user):
self.payload = payload
self.option = ''
self.request = None
@classmethod
def bot(cls, request, option, user):
payload = merge_dicts(dict(request.forms), dict(request.query.decode())) # merge query and form inputs
slf = cls(payload, user)
slf.request = request
slf.option = str(option)
return slf
def test(self): # localhost/api/test
return self.payload
@get('/api/<command>')
@post('/api/<command>')
@get('/api/<command>/<option>')
@post('/api/<command>/<option>')
def routeapi(command='', option=''):
user = User()
wapi = API.bot(request, option, user)
func = getattr(wapi, f"{command}", None)
if callable(func):
result = func()
if result:
if request.method == 'GET':
response.headers['Content-Type'] = 'application/json'
response.headers['Cache-Control'] = 'no-cache'
return {command:json.check(result)}
else:
return {command:None}
Upvotes: 1
Reputation: 409
Your python code seems to be correct, because I tried and it worked. For troubleshooting you can insert:
print(request.headers['Content-Type']) #output should be: application/json'
print(request.body.read()) #output should be like: b'{"key":"value"}'
I used Postman and when I tried the first time, I made the mistake to select body form instead of raw. In Postman you have to write Content-Type:appliction/json in the header manually and insert the json as raw. I assume in Restler it's similar (I never used Restler).
So if you have to configure it manually, make sure that your header contains 'Content-Type':'application/json' And your body is set to raw and look like this. If for example form-data was selected, the manually set header would not be used by postman and print(request.header['Content-Type']) would output something like this: multipart/form-data; boundary=--------------------------742283902180513961059188
Could imagine Restler has the same snare.
Upvotes: 2