How to stream requests to another webserver? - python

I have a url:
myhost.com/largejsondata
In python flask-restful, I want to serve that same largejsondata. How do I stream it? I know within the get function for flask-restful I could do:
class LargeJSON(Resource):
def get(self, todo_id):
#openup a URL that contains a large jsonfile
#stream output as it appears from previous line to the end-users browser
return jsonfile
api.add_resource(LargeJSON, '/largefile')
but how do I properly get it into a response such that it would "stream" output to the browser as output is processed by the "requests.get"?

With flask you can stream data like this
from flask import Response
class LargeJSON(Resource):
def get(self):
jsonfile = {...}
return Response(jsonfile, mimetype='application/json')
api.add_resource(LargeJSON, '/largefile')
from http://flask.pocoo.org/docs/0.10/patterns/streaming/
here you have some doc for the Response object http://flask.pocoo.org/docs/0.10/api/#response-objects

Related

Python Flask : Returned file is not readable

While implementing a rest API in python flask, I have used several options to return a file (any type) , read it and save it to local repository of request but encountered with multiple errors as below:
Case 1:
def download_file():
return send_file('any_file.pdf')
r = requests.get(url = 'http://localhost:5000/download').read()
has responded with a error Response object has no attribute read/text/content
Case 2:
def download_file():
file = open('any_file.pdf','r').read()
return file
r = requests.get(url = 'http://localhost:5000/download')
has responded with a error Return doesn't accept this
So How can I do this as flask is not allowing to return a file without response object and response object is not readable and doesn't support to save that file directly.
The Flask server code in Case 1 is correct. A more complete example:
#app.route('/download')
def download_file():
# Some logic here
send_file('any_file.pdf')
However the Response object returned by requests.get doesn't have a read method. The correct way is to use:
Response.content: Content of the response, in bytes.
So, the client code should be:
r = requests.get('http://localhost:5000/download')
bytes = r.content
# Now do something with bytes, for example save it:
with open('downloaded_file.ext', 'wb') as f:
f.write(bytes)

How to send application/json data with image file in python flask?

I am using the send_from_directory function.
#app.route('/get_data',methods=["GET"])
def get_data():
path = os.path.join(app.config['UPLOAD_FOLDER'], 'uploads')
return send_from_directory(path,"cat.jpg")
HTTP responses should respond with one type of data. It depends on what is your client does.
For example, you can return a json to the client which contains a URL for an image and than the client should fetch that imgae.
Another option is to return an image and return the json data in an HTTP header, or as the first option - have an http header that contains a URL for the JSON data, and make the client fetch the JSON data later.
Good luck :)

Python Falcon - get POST data

I try to use falcon package in my project. Problem is I didn't find a way to get body data from the HTTP post request.
I used code from example, but req.stream.read() doesn't return JSON as expected.
The code is:
raw_json = req.stream.read()
result.json(raw_json, encoding='utf-8')
resp.body = json.dumps(result_json, encoding='utf-8')
How to get the POST data?
Thanks for any help
in falcon 2, if you work with json type, use req.media
for example:
import falcon
from json import dumps
class Resource(object):
def on_post(self, req, resp, **kwargs):
result = req.media
# do your job
resp.body = dumps(result)
api = falcon.API()
api.add_route('/test', Resource())
Little digging into the problem led to the following linked issue on github. It states that falcon framework at least in its version 0.3 and working with Python 2 didn't parse data 'POSTed' as string if they are aptly escaped. We could use more information on what data you are trying to send over POST request and in what format is that being sent, as in if its being send as simple text, or with Header Information Content-Type:application/json, or if its coming through an HTML form.
While the exact issue is not clear from the question I could still suggest trying to use bounded_stream instead of stream as in:
raw_json = req.bounded_stream.read()
result.json(raw_json, encoding='utf-8')
resp.body = json.dumps(result_json, encoding='utf-8')
for the official documentation suggests use of bounded_stream where uncertain conditions such as Content-Length undefined or 0, or if header information is missing altogether.
bounded_stream is described as the following in the official falcon documentation.
File-like wrapper around stream to normalize certain differences between the native input objects employed by different WSGI servers. In particular, bounded_stream is aware of the expected Content-Length of the body, and will never block on out-of-bounds reads, assuming the client does not stall while transmitting the data to the server.
Falcon receives the HTTP requests data as buffer object as passed by WSGI wrapper which receives the data from client, and its possible it doesn't run proper parsing on top of the data to convert to a more usable data structure for performance reasons.
Big thanks to Ryan (and Prateek Jain) for the answer.
The solution is simply to put app.req_options.auto_parse_form_urlencoded=True. For example:
import falcon
class ThingsResource(object):
def on_post(self, req, resp):
value = req.get_param("value", required=True)
#do something with value
app = falcon.API()
app.req_options.auto_parse_form_urlencoded=True
things = ThingsResource()
app.add_route('/things', things)
The field you're looking for is somewhat confusingly named, but it's req.media:
Returns a deserialized form of the request stream. When called, it will attempt to deserialize the request stream using the Content-Type header as well as the media-type handlers configured via falcon.RequestOptions.
If the request is JSON, req.media already contains a python dict.
I have added changes in request.py in falcon framework to parse application/x-www-form-urlencoded and multipart/from-data.
I have raised pull request - https://github.com/falconry/falcon/pull/1236 but it is not yet merged in master.
Check this - https://github.com/branelmoro/falcon
I have added new code to parse POST, PUT and DELETE application/x-www-form-urlencoded and multipart/form-data.
Text fields will be available in req.form_data dictionary and upload file buffer stream will be available in req.files dictionary.
I hope this will help to access POST and GET parameters separately and we will be able to upload files as well.
Good thing about the change is that it will not load entire uploaded file in memory.
Below is sample code to show how to use POST, PUT and DELETE application/x-www-form-urlencoded and multipart/form-data:
import falcon
class Resource(object):
def on_post(self, req, resp):
# req.form_data will return dictionary of text field names and their values
print(req.form_data)
# req.form_data will return dictionary of file field names and
# their buffer class FileStream objects as values
print(req.files)
# support we are uploading a image.jpg in `pancard` file field then
# req.files["pancard"] will be FileStream buffer object
# We can use set_max_upload_size method to set maximum allowed
# file size let say 1Mb = 1*1024*1024 bytes for this file
req.files["pancard"].set_max_upload_size(1*1024*1024)
# We can use uploadto method to upload file on required path (Note: absolute filepath is required)
# This method returns boolean - `True` on successful upload
# and if upload is unsuccessful then it returns `False` and sets error on failure.
path = "/tmp/" + req.files["pancard"].name
response = req.files["pancard"].uploadto("/tmp/" + path)
print(response)
# Once file is uploaded sucessfully, we can check it's size
print(req.files["pancard"].size)
# If file is not uploaded sucessfully, we can check it's error
print(req.files["pancard"].error)
resp.body = "Done file upload"
resp.status = falcon.HTTP_200
# falcon.API instances are callable WSGI apps
app = falcon.API()
things = Resource()
# things will handle post requests to the '/post_path' URL path
app.add_route('/post_path', things)
Do let me know if you have any doubts.
So far... for me bounded_stream.read() and stream.read() both get the posted data as type str. I have only found one way around the issue so far:
def on_post(self, req, resp):
posted_data = json.loads(req.stream.read())
print(str(type(posted_data)))
print(posted_data)
Loading the string into a json dict once the posted data is received is my only solution that I can come up with
Here's something I used while designing an API.
import falcon
import json
class VerifierResource():
def on_post(self, req, resp):
credentials = json.loads(req.stream.read())
if credentials['username'] == USER \
and credentials['passwd'] == PASSWORD:
resp.body = json.dumps({"status": "verified"})
else:
resp.body = json.dumps({"status": "invalid"})
api = falcon.API()
api.add_route('/verify', VerifierResource())
This returns a serialized JSON with corresponding response body.
there is a sample way to get media from body. I use to get the body in the post method:
def on_post(req,resp)
arguments = {}
# get body media on post method
body = req.get_media()
if 'something' in body:
arguments['something'] = body['something']
send body content type Media-Type and print resp or use in code, but if want to send JSON body your code should cover give JSON parameters.
Do let me know if you have any doubts.

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.

How to receive json POST request using web2py

I'm very new to web2py and I'm trying to use it for testing remote server's application (I send a http POST request using python requests with file to process and expect to get counter POST request with report in json and display it in shell or save to file). I found following code for similar issue for XML data
# Controller code:
def index():
response.headers['content-type'] = 'text/xml'
xml = request.body.read() # retrieve the raw POST data
if len(xml) == 0:
xml = '<?xml version="1.0" encoding="utf-8" ?><root>no post data</root>'
return response.render(dict(xml=XML(xml)))
# View code:
{{=xml}}
but I can't make proper changes that will allow to use it for my purpose.
So the question is: how to simply receive json data via POST request and save it directly on my computer or to display it somehow using web2py? No buttons, upload fields, data bases needed.. only to get data from incoming request
In your client:
import requests
:
your_url='http://domain.com/app/controllerfile/j'
r=requests.post(your_url, json=jsonData)
In a web2py controller file 'controllerfile':
import json
def j():
with open("json.txt","a") as f:
json.dump(request.vars,f)
return

Categories