Reading POST body with bottle.py - python

I am having trouble reading a POST request with bottle.py.
The request sent has some text in its body. You can see how it's made here on line 29: https://github.com/kinetica/tries-on.js/blob/master/lib/game.js.
You can also see how it's read on a node-based client here on line 4: https://github.com/kinetica/tries-on.js/blob/master/masterClient.js.
However, I haven't been able to mimic this behavior on my bottle.py-based client. The docs say that I can read the raw body with a file-like object, but I can't get the data neither using a for loop on request.body, nor using request.body's readlines method.
I'm handling the request in a function decorated with #route('/', method='POST'), and requests arrive correctly.
Thanks in advance.
EDIT:
The complete script is:
from bottle import route, run, request
#route('/', method='POST')
def index():
for l in request.body:
print l
print request.body.readlines()
run(host='localhost', port=8080, debug=True)

Did you try simple postdata = request.body.read() ?
Following example shows reading posted data in raw format using request.body.read()
It also prints to the log file (not to the client) raw content of body.
To show accessing of form properties, I added returning "name" and "surname" to the client.
For testing, I used curl client from command line:
$ curl -X POST -F name=jan -F surname=vlcinsky http://localhost:8080
The code which works for me:
from bottle import run, request, post
#post('/')
def index():
postdata = request.body.read()
print postdata #this goes to log file only, not to client
name = request.forms.get("name")
surname = request.forms.get("surname")
return "Hi {name} {surname}".format(name=name, surname=surname)
run(host='localhost', port=8080, debug=True)

Simple script for processing POSTed data. POSTed data are written in a terminal and returned to the client:
from bottle import get, post, run, request
import sys
#get('/api')
def hello():
return "This is api page for processing POSTed messages"
#post('/api')
def api():
print(request.body.getvalue().decode('utf-8'), file=sys.stdout)
return request.body
run(host='localhost', port=8080, debug=True)
Script for POSTing json data to the script above:
import requests
payload = "{\"name\":\"John\",\"age\":30,\"cars\":[ \"Ford\", \"BMW\",\"Fiat\"]}"
url = "localhost:8080/api"
headers = {
'content-type': "application/json",
'cache-control': "no-cache"
}
response = requests.request("POST", url, data=payload, headers=headers)
print(response.text)

Related

Can't access or print any request data with FastAPI

I have a simple FastAPI endpoint, where I want to receive a string value. In this case, I tried it with a JSON body, but basically it doesn't need to be JSON. I really need only a simple string to separate the requests from each other. Unfortunately, I can't access any of the request parameters with a GET method. I also tried POST method instead, but I get an error:
request:
url = "http://127.0.0.1:5000/ping/"
payload=json.dumps({"key":"test"})
headers = {
"Content-Type": "application/json"
}
response = requests.request("POST", url, headers=headers, json=payload)
print(response.text)
api:
#app.get("/ping/{key}")
async def get_trigger(key: Request):
key = key.json()
test = json.loads(key)
print(test)
test2 = await key.json()
print(key)
print(test2)
return
I can't print anything with post or put:
#app.post("/ping/{key}")
async def get_trigger(key: Request):
...
or
#app.put("/ping/{key}")
async def get_trigger(key: Request):
I get a 405 Method not allowed error.
How can I get this fixed?
The 405 Method Not Allowed status code indicates that "the server knows the request method, but the target resource doesn't support this method". You get this error when you attempt, for instance, to send a POST request to a GET route (as shown in your first example). This, however, is not the only issue with your code (on both client and server sides). Below is given an example on how to achieve what you described in the question using Path parameters. The same could be achieved using Query parameters, as well as Request Body. Please have a look at Python requests documentation on how to specify the parameters/body for each case. I would also highly suggest to take the FastAPI tutorial online—you'll find most of the answers you are looking for there.
app.py
from fastapi import FastAPI
app = FastAPI()
#app.get("/ping/{ping_id}")
async def get_trigger(ping_id: str):
return {"ping_id": ping_id}
test.py
import requests
url = 'http://127.0.0.1:8000/ping/test1'
resp = requests.get(url=url)
print(resp.json())

My #app.route decorator is not triggering its function

I am new to python and flask and I am encountering a problem with Flask. I am trying to use a local HTTP POST (webhook) to call a function from another file, but when I do nothing happens.
from flask import Flask
from BotSpeak import main
app = Flask(__name__)
#app.route('/', methods=['POST'])
def respond():
main('hello')
if __name__ == '__main__':
app.run()
This is my very basic Flask app. As you can see it is trying to call the main function from this file (BotSpeak):
from json import dumps
from httplib2 import Http
def main(botmsg):
url = 'PLACEHOLDER FOR GOOGLE CHAT WEBHOOK URL'
bot_message = {
'text' : botmsg}
message_headers = {'Content-Type': 'application/json; charset=UTF-8'}
http_obj = Http()
response = http_obj.request(
uri=url,
method='post',
headers=message_headers,
body=dumps(bot_message),
)
print(response)
if __name__ == '__main__':
main("TEST MESSAGE")
This is the code that shoots local HTTP POSTs to my flask app:
import json
import requests
webhook_url ='http://127.0.0.1:5000/'
data = {PLACE HOLDER FOR JSON DATA}
r = requests.post(webhook_url, data=json.dumps(data), headers={'Content-Type': 'application/json'})
I can call the main function from other files outside the flask app and it'll work but it just wont trigger in the app.route decorator like it should. I really appreciate anyone's help and I encourage you to throw these into VS Code and try it for yourself.
If you're using flask you don't have to manually construct an HTTP response using httplib2 unless you have some very specific use case.
In your case you can just return bot_message since if the return value of a Flask handler is a dict it will convert it to a JSON response (including the correct headers). See
https://flask.palletsprojects.com/en/2.0.x/quickstart/#about-responses
In other words, your entire route handler could be rewritten (based off your example at least):
#app.route('/', methods=['POST'])
def respond():
return {'text': 'hello'}
If you want to pass the handling off to some utility function you can do that too, just make sure it returns a value, and that you in turn return an appropriate value from your respond() function.

POST method adds a header to an xlsx file which corrupts it

I'm trying to send an xlsx file using Python requests library. My request looks like this:
import requests
url_attachment = "http://127.0.0.1:5000/api/attachment"
payload={}
files=[('file',(filename, open(filename,'rb'),'application/octet-stream'))
]
headers = {}
requests.request("POST", url_attachment, headers=headers, data=payload, files=files)
and my mock server looks like this:
import flask
from flask import request
app = flask.Flask(__name__)
app.config["DEBUG"] = True
#app.route('/', methods=['GET'])
def home():
return "<h1>Hello World</h1>"
#app.route('/api/attachment', methods=['POST'])
def get_file():
data = request.files['file']
data.save('the-file.xlsx')
return "ok"
app.run()
The mock server works fine, and the file gets sent correctly.
However, when I call the actual API (that I have no access to), the file gets corrupted. The person who owns the API sent me the corrupted file, and after inspection I can see that the content got wrapped in this:
-------------------------------28947758029299
Content-Disposition: form-data; name="the-file.xlsx"; filename="the-file.xlsx"
Content-Type: application/octet-stream
//content
-------------------------------28947758029299--
Does someone have an idea why this is happening? What can I change in my function to stop this from happening? I have also tried changing the Content-Type to application/vnd.openxmlformatsofficedocument.spreadsheetml.sheet but apparently this results in the same problem.
This has been resolved now. If anyone comes across a similar issue in the future, the cause was the receiving method in the API considering content as 'multi-part form data'.

Executing a post request from a flask route to a remote service

I am encountering some issues to create and execute a post request from one of my flask #app.route. The idea would be to validate and process the data received from a web form by the server, then to call a remote service with this data as a post parameter and finally to be redirected to a new page indicated by this remote service. This is a simplified skeleton :
import requests
#app.route('/test', methods=['POST'])
url = 'http://exemple.com'
headers = {'Content-type': 'text/html; charset=UTF-8'}
response = requests.post(url, data=data, headers=headers)
return x
Any hint about how am I supposed to redirect the user to the remote service using a post request (user that will then again be redirected by the remote service to another page)?
Thank you in advance.
You don't need to redirect user to the remote server. Just send POST request inside this route function and either wait for the response from that server and then render some template to user with received response or send POST request to the desired server in another thread if you know that response can take a lot of time (in this case you can render some page immediately where would be something like "Processing your request" message). In the second case (in case of sending POST request in a different thread) you can show result of the request after reloading page retrieving it from some shared object in main thread that can be modified from the thread you use to send POST request from.
1) Sending request in the route function:
#app.route('/test/', methods=['POST'])
def test():
url = 'http://exemple.com'
headers = {'Content-type': 'text/html; charset=UTF-8'}
response = requests.post(url, data=data, headers=headers)
# wait for the response. it should not be higher
# than keep alive time for TCP connection
# render template or redirect to some url:
# return redirect("some_url")
return render_template("some_page.html", message=str(response.text)) # or response.json()
2) Sending POST request in a different thread (you can use this option if you know that response time is much higher than TCP keep alive time):
from threading import Thread
from flask import Flask, redirect
app = Flask(__name__)
shared_var = None
req_counter = 0
def send_post_req(url, data):
global shared_var
headers = {'Content-type': 'text/html; charset=UTF-8'}
response = requests.post(url, data=data, headers=headers)
shared_var = response.text # or response.json() - it's up to you
#app.route('/test/', methods=['POST'])
def test():
global req_counter
url = 'http://exemple.com'
data = "some data"
if req_counter == 0:
t = Thread(target=send_post_req, args=(url, data))
t.setDaemon(True)
t.start()
req_counter = 1 # use req_counter if you want to send request only one time
if shared_var:
# render some page or redirect to desired url using:
# return redirect(url_for('some_route_name'))
# or
# return redirect("some_url")
return render_template("some_page.html", message=str(shared_var))
return render_template("some_page.html", message="Your request is being processed")
or something like that.
Hope my explanation is clear.
Also you can pay attention to asyncio and Sanic to use asynchronous approach.
Send post request with your user data. Then parse the response to get your link.
import requests
from flask import Flask, redirect
app = Flask(__name__)
#app.route('/test', methods=['POST'])
def test():
# Get your post data from your client request and process it
# Send POST request
r = requests.post("http://example.com", data={your_data})
res_var = response.text #TEXT/HTML response
res_code = response.status_code, response.reason #HTTP
new_destination = '' # get your new link from res_var
Once you have your redirect link... simply use:
return redirect(new_destination, code=302)

Python / Flask -- server is receiving POST requests as GET requests

I created an endpoint in the flask file that looks like this
#app.route("/update", methods=['POST', 'GET'])
def update_func():
results = {
"method": request.method
}
return json.dumps(results)
I tried calling this function using both Postman and python, both are saying Flask is processing it as a get request.
import requests
r = requests.post("http://site.fakeurl.org/update", json={})
print r.json()
Is there a config file I need to change for this process as a POST request?
Is this happening to anyone else?
Did you try using http://www.site.faekurl.org/update ?
Some servers redirect http:// to http://www.-- and as a result, the POST request + it's data gets lost in the process.

Categories

Resources