Python Flask: Send file and variable - python

I have two servers where one is trying to get a file from the other. I am using Flask get requests to send simple data back and forth (strings, lists, JSON objects, etc.).
I also know how to send just a file, but I need to send an error code with my data.
I'm using something along the following lines:
Server 1:
req = requests.post('https://www.otherserver.com/_download_file', data = {'filename':filename})
Server 2:
#app.route('/_download_file', methods = ['POST'])
def download_file():
filename = requests.form.get('filename')
file_data = codecs.open(filename, 'rb').read()
return file_data
Server 1:
with codecs.open('new_file.xyz', 'w') as f:
f.write(req.content)
...all of which works fine. However, I want to send an error code variable along with file_data so that Server 1 knows the status (and not the HTTP status, but an internal status code).
Any help is appreciated.

One solution that comes to my mind is to use a custom HTTP header.
Here is an example server and client implementation.
Of course, you are free to change the name and the value of the custom header as you need.
server
from flask import Flask, send_from_directory
app = Flask(__name__)
#app.route('/', methods=['POST'])
def index():
response = send_from_directory(directory='your-directory', filename='your-file-name')
response.headers['my-custom-header'] = 'my-custom-status-0'
return response
if __name__ == '__main__':
app.run(debug=True)
client
import requests
r = requests.post(url)
status = r.headers['my-custom-header']
# do what you want with status
UPDATE
Here is another version of the server based on your implementation
import codecs
from flask import Flask, request, make_response
app = Flask(__name__)
#app.route('/', methods=['POST'])
def index():
filename = request.form.get('filename')
file_data = codecs.open(filename, 'rb').read()
response = make_response()
response.headers['my-custom-header'] = 'my-custom-status-0'
response.data = file_data
return response
if __name__ == '__main__':
app.run(debug=True)

Related

Flask issue with python: The browser (or proxy) sent a request that this server could not understand

New to Flask and API development, but I'm trying to figure out what's wrong here.
This is my code in my koho-flask.py
from flask import Flask
from flask_restful import reqparse, abort, Api, Resource
import new_model_zero_shot as model
app = Flask(__name__)
api = Api(app)
#create new user_palette object
user = model.user_palette(model.cold_start(), 1)
dish_list = model.dish_list
#user.recommend_meal(['chicken', 'rice'], query_type = ['our_dbs', dish_list], name_csv = None)
# argument parsing
parser = reqparse.RequestParser()
parser.add_argument('query')
# we already have a way to package the output to user
class GetRecipes(Resource):
def get(self):
# use parser and find the user's query
args = parser.parse_args()
user_query = args['query']
recs = (user.recommend_meal(user_query,
query_type = ['our_dbs', dish_list], name_csv = None))
return recs
class Hello(Resource):
def get(self):
return {'data': "Hello wassup!"}
#recipe search endpoint
api.add_resource(GetRecipes, '/')
api.add_resource(Hello, '/')
#%%
if __name__ == '__main__':
app.run(debug=False)
When I run app.run(debug=False), the server starts.
After, I open a test.py file in the same directory and run this.
import requests
url = 'http://127.0.0.1:5000/'
params ={'query': 'chicken'}
response = requests.get(url, params)
a = response.json()
My "a" variable returns a dictionary with the value "The browser (or proxy) sent a request that this server could not understand". This happens whether I test out my dummy "Hello" endpoint or my real "GetRecipes" endpoint.
What am I doing incorrectly?

How to correctly decode text content POST with Flask

What is the correct way to handle character encoding when processing a POST request containing "text/plain" data in Flask?
Test application:
from flask import Flask
from flask import request
app = Flask(__name__)
#app.route("/put-text", method=['POST'])
def hello_world():
print("data", request.data)
return {}
Example request
import requests
url = 'http://127.0.0.1:5000/put-text'
data = 'The quick brown fox jumped over the lazy dog."
headers = {
"Content-Type:":"text/plain",
}
res = requests.post(url, data=data, headers=headers)
With this, the request.data is bytes, and I could decode it relatively easily, but how do I make this code robust to support unicode or different text encodings?
According to the Flask docs, request.get_data(as_text=True) probably does what you want:
#app.route("/put-text", method=['POST'])
def hello_world():
text = request.get_data(as_text=True)
# do something with the `text`...
return {}
Presumably, Flask will decode the text with the correct encoding, as specified by the client in the respective header. Provided the client specifies the correct encoding, of course...

How to get Python dictionary via Flask request files attribute [duplicate]

This question already has answers here:
Read file data without saving it in Flask
(8 answers)
Opening a file that has been uploaded in Flask
(2 answers)
Closed 4 years ago.
With server.py running:
from flask import Flask, request, Response
app = Flask(__name__)
#app.route('/test', methods=['GET','POST'])
def route():
print('got files: %s' % request.files)
return Response()
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
send a request using client.py:
import json, requests
dictionary_1 = {"file": {"url": "https://bootstrap.pypa.io/get-pip.py"}}
files = [('dictionary_1', ('get-pip.py', json.dumps(dictionary_1), 'application/json'))]
response = requests.post('http://127.0.0.1:5000/test', files=files)
Server logs that it received a request:
got files: ImmutableMultiDict([('dictionary_1', <FileStorage: u'get-pip.py' ('application/json')>)])
Apparently, the dictionary_1 was received as FileStorage object.
How to turn the received FileStorage into the Python dictionary?
edited later
The possible duplicate post does not clarify how to send and unpack the Python dictionary object sent via requests(files=list())
This is happening because you're posting files instead of data. This should work:
import flask
app = flask.Flask(__name__)
#app.route('/test', methods=['GET','POST'])
def route():
print('got data: {}'.format(flask.request.json))
return Response()
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
and then send data to your app by
import requests
dictionary_1 = {"file": {"url": "https://bootstrap.pypa.io/get-pip.py"}}
response = requests.post('http://127.0.0.1:5000/test', json=dictionary_1)
In your example there's no need to post the file unless I'm misunderstanding something
Solution # 1:
from flask import Flask, request, Response
import StringIO, json
app = Flask(__name__)
#app.route('/test', methods=['GET','POST'])
def route():
print('got files: %s' % request.files)
for key, file_storage in request.files.items():
string_io = StringIO.StringIO()
file_storage.save(string_io)
data = json.loads(string_io.getvalue())
print('data: %s type: %s' % (data, type(data)) )
return Response()
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
Solution # 2:
from flask import Flask, request, Response
import tempfile, json, os, time
app = Flask(__name__)
#app.route('/test', methods=['GET','POST'])
def route():
print('got files: %s' % request.files)
for key, _file in request.files.items():
tmp_filepath = os.path.join(tempfile.mktemp(), str(time.time()))
if not os.path.exists(os.path.dirname(tmp_filepath)):
os.makedirs(os.path.dirname(tmp_filepath))
_file.save(tmp_filepath)
with open(tmp_filepath) as f:
json_data = json.loads(f.read())
print type(json_data), json_data
return Response(json_data)
if __name__ == '__main__':
app.run('0.0.0.0', 5000)

Dialogflow, How to url encode json as part of the text param?

Im trying to create a script, either by Python or PHP, that can receive JSON from Dialogflow webhooks.
The data value will be from resolvedQuery.
I want to parse the data then send to the below URL:
https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush?text=***JSON GOES HERE ***&deviceId=18c972b753ad&apikey=5b48aed7
The data from resolvedQuery needs to be sent to the about URL where *JSON GOES HERE * is.
Here is the Python code I have been trying:
from flask import Flask
from flask import request
from flask import make_response
import json
import logging as l
import requests
app = Flask(__name__)
#app.route('/')
def hello():
"""Return a friendly HTTP greeting."""
l.info("reached the hello() module...")
return 'Hello Beautiful World!\n'
#app.route('/apiai', methods=['POST'])
def apiai_response():
requests_session = requests.session()
requests_session.headers.update({'Content-Type': 'application/json'})
requests_session.headers.update({'charset':'utf-8'})
post_data = '[{ "resolvedQuery": "" }]'
requests_response = requests_session.post(url="https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush?text=***JSON GOES HERE ***&deviceId=18c972b753ad&apikey=5b48aed7", data=post_data)
print requests_response.content
#app.errorhandler(404)
def page_not_found(e):
"""Return a custom 404 error."""
return 'Sorry, nothing at this URL.', 404
I would try something like this:
import json
import requests
json_string = json.dumps({"hello": "world"})
these_params = {"text": json_string,
"deviceId": "18c972b753ad&apikey=5b48aed7"}
this_url = "https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush"
r.get(url=this_url, params=these_params)
notice it is a get request and not a post request
It is a matter of opinion however using url encoded params on a GET request differ in how stuff gets read. As far as the data field is concerned on a post request, the handling is a bit different.
On a get request it will look something like part of the url as :
text=%7B%22hello%22%3A%22world%22%7D
Where as a post request the server will try to read in a application/json encoded body which expects the "data" to be valid json text.
also note:
How to urlencode a querystring in Python?

Verifying HMAC from Microsoft Teams bot in Python Flask

I am trying to build a Microsoft Teams chat bot using Flask, following the instructions on how to build custom bots. However I am unable to verify the HMAC auth which I really want for security.
Based on guides and documentation I've found I am using the following minimial testing app trying to calculate a HMAC for the incoming request. (Bot name and description DevBot and the key/security_token below for testing).
#!/usr/bin/python
# coding=utf-8
from flask import Flask, request, jsonify
import hmac, hashlib, base64, json
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def webhook():
if request.method == 'POST':
# Authenticate
security_token = b"O5XHU8OSzwx8w9YiM0URkR/Ij4TZZiZUwz7Swc+1hZE="
request_data = request.get_data()
digest = hmac.new(security_token, msg=request_data, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(digest).decode()
# TODO: Verify signature = Authorization header HMAC here
return jsonify({
'type' : 'message',
'text' : "Auth header: {0} <br>Calculated HMAC: {1}".format(request.headers.get('Authorization'), signature),
})
elif request.method == 'GET':
return "Hello World"
if __name__ == '__main__':
app.run(debug=True)
Upon sending the message #DevBot test I get the following hashes back in the reply from the bot, but they aren't matching as expected:
Auth header: HMAC LuDmz97y/Z2KWLIZ1WZASz3HlOEtDCwk5/lL/fK8GqM=
Calculated HMAC: eaxTdJSLuU3Z4l94bxFiWvsBhjNG9SPxwq/UHeR7KcA=
Any ideas or pointers? I've been trying all sorts of stuff with encoding but I have a feeling that Flask might be doing something that modifies the request body or something?
edit 1: small clarification
edit 2: full Flask app example
edit 3: sample bot details, input and output examples
After lots of trial and error and trying to reproduce the C# code example from MS I managed to solve it myself. Here's the solution:
#!/usr/bin/python
# coding=utf-8
from flask import Flask, request, jsonify
import hmac, hashlib, base64, json
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def webhook():
if request.method == 'POST':
# Reply
data = request.get_json()
channel = data['channelId']
message_type = data['type']
sender = data['from']['name']
message_format = data['textFormat']
message = data['text']
# Authenticate
security_token = b"O5XHU8OSzwx8w9YiM0URkR/Ij4TZZiZUwz7Swc+1hZE="
request_data = request.get_data()
digest = hmac.new(base64.b64decode(security_token), msg=request_data, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(digest).decode()
# TODO: verify that HMAC header == signature
return jsonify({
'type' : 'message',
'text' : "auth header: {0} <br>hmac: {1}".format(request.headers.get('Authorization').split(' ')[1], signature),
})
elif request.method == 'GET':
return "Hello World"
if __name__ == '__main__':
app.run(debug=True)
Another option rather than interfacing directly with Microsoft Teams may be to use the Microsoft Bot Connector API.
https://docs.botframework.com/en-us/restapi/connector/
I have a bot working with Microsoft Teams using https://github.com/Grungnie/microsoftbotframework which is validating the JWT that is sent from Microsoft.

Categories

Resources