Using Flask as pass through proxy for file upload? - python

It's for app engine's blobstore since its upload interface generates a temporary endpoint every time. I'd like to take the comlexity out of frontend, Flask would take the post request and forward it to the end point specified by blobstore. Performance and traffic cost is not a concern at all, can someone recommend a most straightforward way to implement?

Looking at the docs for the BlobStore flow it looks like all you need to do is accept the file yourself and then send it on to the endpoint specified by create_upload_url:
#app.route("/upload-complete", methods=["POST"])
def handle_upload_response():
"""This will be called after every upload, but we can ignore it"""
return "Success"
#app.route("/upload", methods=["POST"])
def upload():
fp = request.files["name_of_file"]
url = create_upload_url(url_for('handle_upload_response'))
response = requests.post(url, {'file':
(fp.filename, fp.stream,
fp.content_type, fp.headers)})
if response == "Success":
return "File uploaded successfully"
else:
return "Something didn't work out"

Related

How To Pass Parameters From A User Into An Endpoint To Process Logic From User Input

So I have a Python script that sends a get request to an API and returns information. For educational purposes, I wanted to create an API that I could pass parameters to which would return results from that script. I couldn't really find a similar example on stackoverflow or on the official flask documentation for an example that meets what I'm looking for. Everything I'm seeing is for returning data that you already have existing in a database or in a json file.
Here's an example of the script that I have right now.
script.py
api_url = www.statetaxes.com
parameters = {"taxinfo": "state:Texas county:soaker_county zip_code:78331"}
token = "abcd123456"
response= request.get(api_url, params=parameters, headers={"authentication":token})
print(response.json()) >----- [{tax rate: 1.5%, education_quality:great, crime_rate:0}]
The user would set the parameters in a config file somewhere like so:
[taxinfo]
county: some_county
zip_code: 12345
I'd want to read that text file, grab those parameters and push it to an API endpoint. I can figure out the part on how to read those parameters and send it to the API, but I'm not sure how I'm supposed to be configuring the rest of my FLASK app. I'd assume that I'd have to take those parameters which I'm reading from the text file as an argument. Am I on the right path here?
#app.route('/taxes/<taxinfo>', methods = ['GET', 'POST', 'PUT' ])
def state_taxes(taxinfo):
def api_call():
api_url = www.statetaxes.com
parameters=taxinfo
token = "abcd123456"
response= request.get(api_url, params=parameters, headers={"authentication":token})
return api_call
if __name__== "__main__":
app.run(debug=True)

Setting a parameter in Post body to header when accessing Get endpoint in Flask?

I am working on a simple service with my show_greeting endpoint handling Get request while set_greeting is my Post.
The purpose of this app is that when "header_message: {header parameter}" is sent to set_greeting, {header parameter} will be returned in the header for responses to show_greeting and to reset {header parameter}, "clear" would reset header_message and header.
I have tried using global variables but encountered an error with shadowing from outside the scope and am not sure which approach to take for this. For now, I would like to learn how to return {header parameter} from my /show_greeting endpoint.
Edit: The /show_greeting endpoint returns holiday_message from the request. The header that I would like to send in addition to holiday_message is "header_message".
My code is as follows:
from flask import Flask, request, make_response, Response
app = Flask(__name__)
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
(I do not know how to set header here from header_message in set_greeting)
return received['holiday_message']
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.args
if 'header_message' in posted:
(I attempted save_message = posted['header_message'] here but this approach failed)
return "Header Message Set"
else:
return "Please Send A Header Message"
if __name__ == '__main__':
app.run()
My recommendation is to use the session object. It stores the data in a cookie, which is sent with every request.
If a cookie is not desired, there are other options for saving sessions. For this, however, an extension will be necessary.
Saving with global variables should also work, but is not recommended.
A file or a database can also be used if the data is to be saved across multiple requests from many users.
The data of the post body can be accessed via request.form, while the url parameters of a get request can be queried via request.args.
from flask import Flask
from flask import request, session
app = Flask(__name__)
app.secret_key = b'your secret here'
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
# get the saved message or an empty string if no message is saved
header_message = session.get('header_message', '')
return f"{received['holiday_message']} - {header_message}"
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.form
if 'header_message' in posted:
# store the message
session['header_message'] = posted['header_message']
return "Header Message Set"
else:
# clear the message
session.pop('header_message', None)
return "Please Send A Header Message"
Much success in your further steps.
If I understood your problem, you can work with "g" the flask global object.
Check this code, I expect it will fix your issue.
from flask import g # Added
from flask import Flask, request, make_response, Response
app = Flask(__name__)
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
return g.saved_message # Modified
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.args
if 'message' in posted:
g.saved_message = posted['request'] # Added
return "Message Set"
else:
return "Please Send A Greeting Message"
if __name__ == '__main__':
app.run()

Flask returns a response without even checking files

I am trying to understand more precisely how Http connections work with Flask, so I tried writing a very simple app and another simple connection with requests:
app = Flask('file-streamer')
#app.route("/uploadDumb", methods=["POST"])
def upload_dumb():
print("Hello")
return Response(status=200)
Then I am sending a big file (1.5 GB) to the endpoint with requests :
url = "http://localhost:5000/uploadDumb"
values = {"file": open(file, "rb")}
r = requests.post(url=url, data={}, files=values)
I expected Flask to wait for the whole file to be sent, even if the file is useless. However this is not what's happening, instead Flask returns a 200 response very quickly, which causes a BrokenPipeError with my second script.
Can someone explain to me what is happening here ?
I suppose that happens because Flask body-parsing is lazy (which is a good thing, actually). So when request comes, Flask only reads headers until body is accessed somehow (through request.data, request.files, request.json(), etc.). So to trigger a full body (and file) upload, try accessing request body to make Flask parse a file, like:
_ = request.data

Redirect to external url while sending a JSON object or String from flask app

I have a flask application where I need to redirect to an URL outside flask root path (www.externalurl.com). This URL, on the client side, requires consuming some information (either a JSON object or a String). How to send this information along while redirecting?
Consider below example,
from flask import Flask,redirect
app = Flask(__name__)
#app.route('/<some_path>')
def some_method():
# some_info = {'key1' : 'value1'}
response = redirect("http://www.externalurl.com", code=302)
return response
I have come across setting headers & cookies. But, I came to know that the cookie size should be less than 4k and I don't want this limitation. Are there any standard ways of doing this?
You mentioned that you have control over externalurl.com
You can associate some key or hash with the JSON object and store them to be retrieved by a script on externalurl.com later.
So, store it in a database at example.com and setup an endpoint for it to be requested later.
#app.route('/<some_path>')
def some_method():
some_info = {'key1' : 'value1'}
db_entry = JsonInfo(some_info)
db.add(db_entry)
db.commit()
hash = db_entry.hash
response = redirect("http://www.externalurl.com?hash={}".format(hash), code=302)
return response
#app.route('/get-json')
def get_json():
hash = request.args.get("hash")
json_info = JsonInfo.query.filter_by(hash=hash).first()
return jsonify(json_info)
Then on the side of externalurl.com parse the hash from the get request, then use that to make a request to https://example.com/get-json?hash=abc123 to retreive the info.

Trying to write a unit test for file upload to a django Restless API

I'm writing a fairly small lightweight REST api so I chose restless as the quickest/easiest support for that. I didn't seem to need all the complexity and support of the django-REST module. My service will only received and send json but users need to upload files to one single endpoint. Currently my view/api code for the file upload looks like:
class SubmissionCreate(Endpoint):
def post(self, request):
# get the data from the post request
data = {}
data['input_data'] = request.FILES['input_data'].read().decode('UTF-8')
data['submission_name'] = request.FILES['submission_name'].read().decode('UTF-8')
submission_form = SubmissionForm(data)
if submission_form.is_valid():
s = submission_form.save()
return {'message': 'file uploaded'}
else:
return {'error': 'Input information is not correctly formatted'}
I also wrote a small client with Requests to upload files
import os
import requests
import json
url = 'http://127.0.0.1:8000/submission/create/'
payload = {'input_data': ('input.txt', open('./static/files/file1.txt', 'rb')), 'submission_name': 'test'}
r = requests.post(url, files=payload)
This works great and I can push files to the database with my client. But obviously I want some proper testing before I give this any more complex behaviour, so I looked at the docs and wrote the following test
class SubmissionCreateTests(TestCase):
def test_submissioncreate_will_accept_data(self):
f = SimpleUploadedFile("file.txt", bytes("file_content", 'utf-8'))
response = self.client.post(reverse('submission_data'),
{'input_data': f, 'submission_name': 'test'})
self.assertEqual(response.status_code, 200)
However this produces the following error:
django.utils.datastructures.MultiValueDictKeyError: "'submission_name'"
If I set the content_type to 'application/x-www-form-urlencoded' I get the same error.
If I set the content_type to 'multipart/form-data' I get a 400 error but the tests run with no exception thrown.
I tried to fix this but in the end it was quicker and easier to switch to the Django-REST framework. The docs are so much better for Djano-REST that it was trivial to set it up and build tests. There seem to be no time savings to be had with regards either the restless or django-restless modules for django.

Categories