Josias Aurel
Josias Aurel

Reputation: 47

Handle post request in flask and bottle

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

Answers (2)

eatmeimadanish
eatmeimadanish

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

Lukas
Lukas

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' enter image description here And your body is set to raw and look like this. enter image description here 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

Related Questions