this is a two-part question: I have seen individual pieces discussed, but can't seem to get the recommended suggestions to work together. I want to create a web service to store images and their metadata passed from a caller and run a test call from Postman to make sure it is working. So to pass an image (Drew16.jpg) to the web service via Postman, it appears I need something like this:
For the web service, I have some python/flask code to read the request (one of many variations I have tried):
from flask import Flask, jsonify, request, render_template
from flask_restful import Resource, Api, reqparse
...
def post(self, name):
request_data = request.get_json()
userId = request_data['UserId']
type = request_data['ImageType']
image = request.files['Image']
Had no problem with the data portion and straight JSON but adding the image has been a bugger. Where am I going wrong on my Postman config? What is the actual set of Python commands for reading the metadata and the file from the post? TIA
Pardon the almost blog post. I am posting this because while you can find partial answers in various places, I haven't run across a complete post anywhere, which would have saved me a ton of time. The problem is you need both sides to the story in order to verify either.
So I want to send a request using Postman to a Python/Flask web service. It has to have an image along with some metadata.
Here are the settings for Postman (URL, Headers):
And Body:
Now on to the web service. Here is a bare bones service which will take the request, print the metadata and save the file:
from flask import Flask, request
app = Flask(__name__)
# POST - just get the image and metadata
#app.route('/RequestImageWithMetadata', methods=['POST'])
def post():
request_data = request.form['some_text']
print(request_data)
imagefile = request.files.get('imagefile', '')
imagefile.save('D:/temp/test_image.jpg')
return "OK", 200
app.run(port=5000)
Enjoy!
Make sure `request.files['Image'] contains the image you are sending and follow http://flask.pocoo.org/docs/1.0/patterns/fileuploads/ to save the file to your file system. Something like
file = request.files['Image']
file.save('./test_image.jpg')
might do what you want, while you will have to work out the details of how the file should be named and where it should be placed.
Related
I built a simple flask server in order to automate python scripts based on an HTTP POST Request coming in from an outside service. This service sends these requests in response to events that we have created based on data conditions. All the flask server needs to do is parse the requests, and take the numerical value stored in the request and use it to run the corresponding python script. At one point, this system was working consistently. Fast forward a few months, the server was no longer working when tried again. No changes were made to the code. According to wireshark, The computer hosting the flask server is receiving the request to the correct port, but the request is now timing out. The host system is failing to respond to the request. Any Idea what is going on here? The firewall has temporarily been turned off.
Alternatively, is there another better package to achieve this goal with?
from flask import Flask, request
import threading
import runpy
app = Flask(__name__)
#app.route('/', methods=['POST'])
def PostHandler():
directory = {}
with open(r"M:\redacted") as f:
for line in f:
(key,val) = line.split()
directory[int(key)] = val
print(directory)
path = r"M:\redacted"
content = request.json
content = content['value']
print(content)
sel_script = int(content)
print(directory[sel_script])
runpy.run_path(path_name=path + directory[sel_script])
return
app.run(host="10.244.xx.xx", port=8080, threaded=True)
I am creating a REST API using python flask. The API is ready and works on port number 8000 of my localhost. Now I intend to give this REST API a user friendly interface for which I decided to go with python - restplus. I thought of calling this service (running on 8000) internally from swagger application running on 5000
I was able to create the basic structure of the API (Swagger). The code for which looks like this:
import flask
from flask import Flask, request
from flask_restplus import Resource, Api
app = Flask(__name__)
api = Api(app)
#api.route('/HybridComparator/<string:url2>/<string:url1>')
class HybridComparator(Resource):
def get(self, url1, url2):
print(url1)
print(url2)
return url1 + ' ' + url2
if __name__ == '__main__':
app.run(debug=True)
The application as a whole runs seamlessly (with random strings as parameters) on port 5000. But when the URLs I pass are actual links, the application returns a response of 404 - Not found. Further to my investigation I realized the culprit being '/' embedded within the links I try to provide. Is there a way to handle URLs in particular?
Should I encode them before sending a request. (This will make my parameters look ugly). Is there something I am missing?
This is an entirely old question and I am sure you solved your problem by now.
But for new searchers, this may come in handy;
replace <string:url2>/<string:url1> with <path:url2>/<path:url1>
it seems that :
#api.route('/HybridComparator/<path:url2>/<path:url1>')
should fix it ,it fixes the 404 but i am getting only "http:/" part of the param
I've built an API using python flask, with the CORS plugin. Cross origin requests are fine when using GET methods. In addition POST is fine with IE but not chrome, where I see a OPTIONS request and no subsequent POST. After reading lots about CORS and most of the documentation, I think my use of CORS should handle the OPTIONS request, but not luck.
My API code:
from flask import Flask, Response, jsonify, request
from flask.ext.cors import CORS, cross_origin #Used to get around the Cross domain request issue (e.g. when making a request to an entity of another origin (ip/domain + port))
from DB import webAPIInterface
import json
DB = webAPIInterface()
app = Flask(__name__)
CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'
#app.route('/api/v1.0/alarmArmedState', methods=['POST'])
#cross_origin() # allow all origins all methods.
def toggleAlarmArmedState():
#DO stuff......
Note, I added the app.config['CORS_HEADERS'... and #cross_origin() later after lots of googling. Didn't seem to make much difference. I also tried different values here, including a content type of JSON (as that's what I use).
I also tried adding this:
#app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers', 'Content- Type,Authorization')
response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
return response
Please let me know your thoughts. Is there something obvious I'm missing to handle OPTIONS calls?
I'm experimenting with a Cloud Endpoint API in Python on App Engine, but I'm having some difficulty getting a simple request parameter.
I'm more familiar with Cloud Endpoints for Java, so I'm possibly (probably) missing something obvious. All I'm trying to do in this example is return the ServiceInfo object with an id specified in the url path, /services/<id>
I have a trivial response message class:
class ServiceInfo(messages.Message):
crs = messages.StringField(1)
services = messages.StringField(2)
and API class:
#endpoints.api(name='myApi', version='v1', description='My API', audiences=[endpoints.API_EXPLORER_CLIENT_ID])
class MyApi(remote.Service):
#No request body, but need to capture the id from the URL
ID_RESOURCE = endpoints.ResourceContainer(
message_types.VoidMessage,
id=messages.StringField(1, variant=messages.Variant.STRING, required=True))
#endpoints.method(ID_RESOURCE, ServiceInfo, path='services/{id}', http_method='GET', name='station.getServices')
def get_services(self, request):
print request.id
...
return ServiceInfo(crs=request.id, services=...)
Now, if I make a request through API explorer and enter ABC as the id field, I see this request:
GET /_ah/api/myApi/v1/services/ABC
But the response says
"Error parsing ProtoRPC request (Unable to parse request content: Message CombinedContainer is missing required field id)"
And when I print request.id, I get None.
All I'm trying to do is get the id from the path - am I missing something really obvious?
Thanks!
After re-visiting this after a couple of days, I restarted the local dev server (using gcloud preview app run ...) and it seems to now be working with the id in the url path (with no code changes) so perhaps the dev server environment had been caching an old version of one of my files?
I am running a webserver based on Flask, which serves a resource being versioned (e.g. installation file of some versioned program). I want to serve my HTTP client with new resource only in case, it already does not have the current version available. If there is new version, I want the client to download the resource and install it.
my Flask server looks like this
import json
import redis
import math
import requests
from flask import Flask,render_template,request
app=Flask(__name__)
#app.route('/version', methods=['GET','POST'])
def getversion():
r_server=redis.Redis("127.0.0.1")
if request.method == 'POST':
jsonobj_recieve=request.data
data=json.loads(jsonobj)
currentversion=r_server.hget('version')
if data == currentversion:
#code to return a 'ok'
else:
#code to return 'not ok' also should send the updated file to the client
else:
return r_server.hget('version')
if __name__ == '__main__':
app.run(
debug=True,
host="127.0.0.1",
port=80
)
my client is very basic:
import sys
import json
import requests
url="http://127.0.0.1/version"
jsonobj=json.dumps(str(sys.argv[1]))
print jsonobj
r=requests.post(url,data=jsonobj)
I will likely have to recode the entire client, this is not a problem but I really have no idea where to start....
Requirements Review
have web app, serving a versioned resource. It can be e.g. file with an applications.
have client, which allows fetching the resource only in case, the version of resource on the server and what client has locally already available differ
the client is aware of version string of the resource
allow client to learn new version string if new version is available
HTTP like design of your solution
If you want to allow downloading an application only in case, the client does not have it already, following design could be used:
use etag header. This usually contains some string describing unique status of resource you want to get from that url. In your case it could be current version number of your application.
in your request, use header "if-none-match", providing version number of your application present at client. This will result in HTTP Status code 306 - Not Modified in case, your client and server share the same version of resource. In case it differs, you would simply provide the content of the resource and use it. Your resource shall also denote in etag current version of the resource and your client shall take note of it, or find new version name from other sources (like from the downloaded file).
This design follows HTTP principles.
Flask serving resource with declaring version in etag
This is focusing on showing the principle, you shall elaborate on providing real content of the resource.
from flask import Flask, Response, request
import werkzeug.exceptions
app = Flask(__name__)
class NotModified(werkzeug.exceptions.HTTPException):
code = 304
def get_response(self, environment):
return Response(status=304)
#app.route('/download/app')
def downloadapp():
currver = "1.0"
if request.if_none_match and currver in request.if_none_match:
raise NotModified
def generate():
yield "app_file_part 1"
yield "app_file_part 2"
yield "app_file_part 3"
return Response(generate(), headers={"etag": currver})
if __name__ == '__main__':
app.run(debug=True)
Client getting resource only, if it is new
import requests
ver = "1.0"
url = "http://localhost:5000/download/app"
req = requests.get(url, headers={"If-None-Match": ver})
if req.status_code == 200:
print "new content of resource", req.content
new_ver = req.headers["etag"]
else:
print "resource did not change since last time"
Alternative solution of web part using web server (e.g. NGINX)
Assuming the resource is static file, which updates only sometime, you shall be able configuring your web server, e.g. NGINX, to serve that resource and declaring in your configuration explicit value for etag header to the version string.
Note, that as it was not requested, this alternative solution is not elaborated here (and was not tested).
Client implementation would not be modified by that (here it pays back the design is following HTTP concepts).
There are multiple ways of achieving this but as this is a Flask app, here's one using HTTP.
If the version is OK, just return a relevant status code, like a 200 OK. You can add a JSON response in the body if that's necessary. If you return a string with flask, the status code will be 200 OK and you can inspect that in your client.
If the version differs, return the URL where the file is located. The client will have to
download the file. That's pretty simple using requests. Here's a typical example for downloading file by streaming requests:
def get(url, chunk_size=1024):
""" Download a file in chunks of n bytes """
fn = url.split("/")[-1] # if you're url is complicated, use urlparse.
stream = requests.get(url, stream=True)
with open(fn, "wb") as local:
for chunk in stream.iter_content(chunk_size=chunk_size):
if chunk:
f.write(chunk)
return fn
This is very simplified. If your file is not static and cannot live on the server (like software update patches probably shouldn't) then you'll have to figure out a way to get the file from a database or generate it on the fly.