Flask Client does not retreive response - python

I am trying to retrieve data from server through a client using requests.post
task id id a random id
here is the server
from json import dumps
from werkzeug.exceptions import BadRequest
from flask import (
Flask,
Response,
request
)
from utils import get_random_id
app = Flask(__name__)
tasks = {
get_random_id(): {
"task": "write server mock",
"completed": True
}
#print(get_random_id())
}
#app.route(
'/tasks',
methods=['GET']
)
def list_tasks():
li = [
{
"id": task_id,
"task": values["task"],
"completed": values["completed"]
} for task_id, values in tasks.iteritems()
]
#print(li)
return Response(
dumps(li),
mimetype='application/json'
)
if __name__ == "__main__":
pass
Here is the client:
from flask import Flask,request
#importing server file that i made
import server
app = Flask(__name__)
app.testing = True
#app.route('/')
def index():
return request.url
response = app.test_client(server)
print(response)
I want to retrieve the posts in my python file all I manage to generate is response of the client
How do I get the table?
Do I use API Endpoints?
Please help me fix this error

You need to use some HTTP library for making a request.
Try http://docs.python-requests.org/en/master/
Example:
import requests
r = requests.get('{your server url}/tasks')

test_client method will create a client for you. And then you should use that client to query your server (either POST or GET or whatever). So you could use something like the following:
with server.test_client() as c:
resp = c.get('/tasks')
data = json.loads(resp.data)
print(data)

Related

Flutter not sending post request to flask server

I am trying to send a form data through flutter to my flask webserver but the flask server is not receiving the request. Below is my flutter code
class FetchData {
Future<Price> getData(
int area,
int bedrooms,
double? psf,
int feature_score,
double? locationPremium,
) async {
const uri = 'http://127.0.0.1:5000/predict';
var map = <String, dynamic>{};
map['area'] = area;
map['bedrooms'] = bedrooms;
map['psf'] = psf;
map['feature_score'] = feature_score;
map['location_premium'] = locationPremium;
map['log_premium'] = log(locationPremium!);
http.Response response = await http.post(Uri.parse(uri), body: map);
if (response.statusCode == 200) {
return Price.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load post');
}
}
}
And below is my app.py code
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
import pickle
import numpy as np
model=pickle.load(open('model1.pkl','rb'))
app = Flask(__name__)
#app.route('/')
def home():
return "Hello World"
#app.route('/predict',methods=['POST'])
def predictor():
area=int(request.form['area'])
bedrooms=int(request.form['bedrooms'])
psf=float(request.form['psf'])
feature_score=int(request.form['feature_score'])
LocationPremium=float(request.form['LocationPremium'])
LogPremium=float(request.form['LogPremium'])
query=np.array([[area,bedrooms,psf,feature_score,LocationPremium,LogPremium]])
result= model.predict(query)[0]
return jsonify({'prediction':str(result)})
if __name__ == '__main__':
app.run(debug=True)
app.run(host='0.0.0.0', port=5000)
I have tried sending the request through postman and the server is receiving the POST request
You need to add headers to your post request.
Future post( Uri url, { Map ? headers, Object? body, Encoding? encoding, })

FastApi - api key as parameter secure enough

i am new in this part of programming and i have few questions. First of all my project. At one side i have a Flutter App and at the other side a MS SQL Server with data. This data i need on my device logically. I read the best way is to use FastAPI, its easy and has a good performance but i am not sure about security. I read something about OAuth2 but it looks to much because just one user will have permission to use the data (the server owner). Is it possible just to use a simple api key as a parameter? Something like this...
from fastapi import FastAPI
from SqlServerRequest import SqlServerRequest
app = FastAPI()
#app.get("/openOrders/{key}")
async def openOrders(key):
if key == "myverysecurekey":
return "SQLDATA"
else
return "Wrong key"
That way works but i am not sure about the security
What would you say?
I have been dealing with the same issue for a while. Instead of using a oauth I needed a simple X-API-Key in the header.
You can do that with the following code
from fastapi import FastAPI, Depends
from fastapi.security import APIKeyHeader
import os
os.environ['API-KEY'] = '1234'.
# You would use as an environment var in real life
X_API_KEY = APIKeyHeader(name='X-API-Key')
def api_key_auth(x_api_key: str = Depends(X_API_KEY)):
""" takes the X-API-Key header and validate it with the X-API-Key in the database/environment"""
if x_api_key != os.environ['API-KEY']:
raise HTTPException(
status_code=401,
detail="Invalid API Key. Check that you are passing a 'X-API-Key' on your header."
)
app = FastAPI()
#app.get("/do_something", dependencies=[Depends(api_key_auth)])
async def do_something():
return "API is working OK."
If your use case is just to serve a single user, and is not mission-critical, this might be a good way to start.
main.py
import os
import uvicorn
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from starlette import status
# Use token based authentication
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Ensure the request is authenticated
def auth_request(token: str = Depends(oauth2_scheme)) -> bool:
authenticated = token == os.getenv("API_KEY", "DUMMY-API-KEY")
return authenticated
app = FastAPI()
#app.get("/openOrders")
async def open_orders(authenticated: bool = Depends(auth_request)):
# Check for authentication like so
if not authenticated:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated")
# Business logic here
return {"message": "Authentication Successful"}
if __name__ == '__main__':
uvicorn.run("main:app", host="127.0.0.1", port=8080)
You can run this using python main.py
The client can then make requests like so:
import requests
url = "http://127.0.0.1:8080/openOrders"
payload={}
# The client would pass the API-KEY in the headers
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer DUMMY-API-KEY'
}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
Client code in Dart
final response = await http.get(
Uri.parse('http://127.0.0.1:8080/openOrders'),
// Send authorization headers to the backend.
headers: {
HttpHeaders.authorizationHeader: 'Bearer DUMMY-API-KEY',
},
);

filter out json with json.loads(r.text)

I am trying to fetch rest api from XYZ provider inside python/flask webApp I used this as example/guide https://help.parsehub.com/hc/en-us/articles/217751808-API-Tutorial-How-to-get-run-data-using-Python-Flask
I dont have api key but auth token so this is code I am using and returns alldata
`from flask import Flask, render_template
import requests
import json
app = Flask(__name__)
#app.route('/')
def homepage():
headers = {
"Accept": "application/json",
"Authorization": "Bearer MYKEY"
}
r = requests.get(
'https://st1.example.com/restapi/domains/', headers=headers)
return json.loads(r.text)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
`
and this return all json data like this
json image
Id like to filter is to display only one part "domain" and "destination" but when adding code like this
r = requests.get(
'https://st1.example.com/restapi/domains/', headers=headers)
return json.loads(r.text)['destination']
I keep getting "Key error "destination" " or whatever I put in there
error
Your json returns data which is an array that contains destination. Assuming you want the first destination, try
return json.loads(r.text)['data'][0]['destination']
Or it coulde be written as:
results = json.loads(r.text)
return results['data'][0]['destination']
It seems like 'destination' is stored under 'data' in the JSON response. Also, you should just call r.json() instead of json.loads(r.text). You should also jsnoify your response.
from flask import Flask, render_template, jsonify
import requests
app = Flask(__name__)
#app.route('/')
def homepage():
headers = {
"Accept": "application/json",
"Authorization": "Bearer MYKEY"
}
r = requests.get(
'https://st1.example.com/restapi/domains/', headers=headers
)
destination = r.json()['data'][0]['destination']
return jsonify(destination), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)

Problems while making requests to a flask restful app

I have the following flask api, which just returns echo of its input:
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class query(Resource):
def get(self, a_string):
return{
'original': a_string,
'echo': a_string
}
api.add_resource(query,'/echo/<a_string>')
if __name__ == '__main__':
app.run()
Then, when I try to use python requests to make queries to my api:
import json
def query(text):
payload = {'echo': str(text)}
headers = {'content-type': 'application/x-www-form-urlencoded'}
r = requests.request("POST", 'http://127.0.0.1:5000', data=payload, headers=headers)
print(r)
#data = json.loads(r.text)
#return data
query('hi')
I keep getting:
<Response [404]>
Any idea of how to fix this issue? Interestingly when I go to my browser and do:
http://127.0.0.1:5000/echo/hi
I get:
{"original": "hi", "echo": "hi"}
But sending a POST to / with a payload of {"echo": whatever} is not at all the same as sending a GET to /echo/whatever. Your API expects the latter.
def query(text):
r = requests.get("http://127.0.0.1:5000/echo/{}".format(text))
Or, change your API so it does expect that:
class query(Resource):
def post(self):
a_string = request.form["echo"]
return {
'original': a_string,
'echo': a_string
}
api.add_resource(query, '/')

How to enable CORS in flask

I am trying to make a cross origin request using jquery but it keeps being reject with the message
XMLHttpRequest cannot load http://... No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin ... is therefore
not allowed access.
I am using flask, heroku, and jquery
the client code looks like this:
$(document).ready(function() {
$('#submit_contact').click(function(e){
e.preventDefault();
$.ajax({
type: 'POST',
url: 'http://...',
// data: [
// { name: "name", value: $('name').val()},
// { name: "email", value: $('email').val() },
// { name: "phone", value: $('phone').val()},
// { name: "description", value: $('desc').val()}
//
// ],
data:"name=3&email=3&phone=3&description=3",
crossDomain:true,
success: function(msg) {
alert(msg);
}
});
});
});
on the heroku side i am using flask and it is like this
from flask import Flask,request
from flask.ext.mandrill import Mandrill
try:
from flask.ext.cors import CORS # The typical way to import flask-cors
except ImportError:
# Path hack allows examples to be run without installation.
import os
parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.sys.path.insert(0, parentdir)
from flask.ext.cors import CORS
app = Flask(__name__)
app.config['MANDRILL_API_KEY'] = '...'
app.config['MANDRILL_DEFAULT_FROM']= '...'
app.config['QOLD_SUPPORT_EMAIL']='...'
app.config['CORS_HEADERS'] = 'Content-Type'
mandrill = Mandrill(app)
cors = CORS(app)
#app.route('/email/',methods=['POST'])
def hello_world():
name=request.form['name']
email=request.form['email']
phone=request.form['phone']
description=request.form['description']
mandrill.send_email(
from_email=email,
from_name=name,
to=[{'email': app.config['QOLD_SUPPORT_EMAIL']}],
text="Phone="+phone+"\n\n"+description
)
return '200 OK'
if __name__ == '__main__':
app.run()
Here is what worked for me when I deployed to Heroku.
http://flask-cors.readthedocs.org/en/latest/
Install flask-cors by running -
pip install -U flask-cors
from flask import Flask
from flask_cors import CORS, cross_origin
app = Flask(__name__)
cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'
#app.route("/")
#cross_origin()
def helloWorld():
return "Hello, cross-origin-world!"
I've just faced the same issue and I came to believe that the other answers are a bit more complicated than they need to be, so here's my approach for those who don't want to rely on more libraries or decorators:
A CORS request actually consists of two HTTP requests. A preflight request and then an actual request that is only made if the preflight passes successfully.
The preflight request
Before the actual cross domain POST request, the browser will issue an OPTIONS request. This response should not return any body, but only some reassuring headers telling the browser that it's alright to do this cross-domain request and it's not part of some cross site scripting attack.
I wrote a Python function to build this response using the make_response function from the flask module.
def _build_cors_preflight_response():
response = make_response()
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add("Access-Control-Allow-Headers", "*")
response.headers.add("Access-Control-Allow-Methods", "*")
return response
This response is a wildcard one that works for all requests. If you want the additional security gained by CORS, you have to provide a whitelist of origins, headers and methods.
This response will convince your (Chrome) browser to go ahead and do the actual request.
The actual request
When serving the actual request you have to add one CORS header - otherwise the browser won't return the response to the invoking JavaScript code. Instead the request will fail on the client-side. Example with jsonify
response = jsonify({"order_id": 123, "status": "shipped"})
response.headers.add("Access-Control-Allow-Origin", "*")
return response
I also wrote a function for that.
def _corsify_actual_response(response):
response.headers.add("Access-Control-Allow-Origin", "*")
return response
allowing you to return a one-liner.
Final code
from flask import Flask, request, jsonify, make_response
from models import OrderModel
flask_app = Flask(__name__)
#flask_app.route("/api/orders", methods=["POST", "OPTIONS"])
def api_create_order():
if request.method == "OPTIONS": # CORS preflight
return _build_cors_preflight_response()
elif request.method == "POST": # The actual request following the preflight
order = OrderModel.create(...) # Whatever.
return _corsify_actual_response(jsonify(order.to_dict()))
else:
raise RuntimeError("Weird - don't know how to handle method {}".format(request.method))
def _build_cors_preflight_response():
response = make_response()
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add('Access-Control-Allow-Headers', "*")
response.headers.add('Access-Control-Allow-Methods', "*")
return response
def _corsify_actual_response(response):
response.headers.add("Access-Control-Allow-Origin", "*")
return response
OK, I don't think the official snippet mentioned by galuszkak should be used everywhere, we should concern the case that some bug may be triggered during the handler such as hello_world function. Whether the response is correct or uncorrect, the Access-Control-Allow-Origin header is what we should concern. So, it is very simple, just like the snippet bellow:
# define your bluprint
from flask import Blueprint
blueprint = Blueprint('blueprint', __name__)
# put this sippet ahead of all your bluprints
# blueprint can also be app~~
#blueprint.after_request
def after_request(response):
header = response.headers
header['Access-Control-Allow-Origin'] = '*'
# Other headers can be added here if needed
return response
# write your own blueprints with business logics
#blueprint.route('/test', methods=['GET'])
def test():
return "test success"
That is all~~
If you want to enable CORS for all routes, then just install flask_cors extension (pip3 install -U flask_cors) and wrap app like this: CORS(app).
That is enough to do it (I tested this with a POST request to upload an image, and it worked for me):
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # This will enable CORS for all routes
Important note: if there is an error in your route, let us say you try to print a variable that does not exist, you will get a CORS error related message which, in fact, has nothing to do with CORS.
I resolved this same problem in python using flask and with this library.
flask_cors
in file init.py:
#pip install flask_cors
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
cors = CORS(app, resource={
r"/*":{
"origins":"*"
}
})
and its all.
Reference: https://flask-cors.readthedocs.io/en/latest/
Improving the solution described here: https://stackoverflow.com/a/52875875/10299604
With after_request we can handle the CORS response headers avoiding to add extra code to our endpoints:
### CORS section
#app.after_request
def after_request_func(response):
origin = request.headers.get('Origin')
if request.method == 'OPTIONS':
response = make_response()
response.headers.add('Access-Control-Allow-Credentials', 'true')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
response.headers.add('Access-Control-Allow-Headers', 'x-csrf-token')
response.headers.add('Access-Control-Allow-Methods',
'GET, POST, OPTIONS, PUT, PATCH, DELETE')
if origin:
response.headers.add('Access-Control-Allow-Origin', origin)
else:
response.headers.add('Access-Control-Allow-Credentials', 'true')
if origin:
response.headers.add('Access-Control-Allow-Origin', origin)
return response
### end CORS section
All the responses above work okay, but you'll still probably get a CORS error, if the application throws an error you are not handling, like a key-error, if you aren't doing input validation properly, for example. You could add an error handler to catch all instances of exceptions and add CORS response headers in the server response
So define an error handler - errors.py:
from flask import json, make_response, jsonify
from werkzeug.exceptions import HTTPException
# define an error handling function
def init_handler(app):
# catch every type of exception
#app.errorhandler(Exception)
def handle_exception(e):
#loggit()!
# return json response of error
if isinstance(e, HTTPException):
response = e.get_response()
# replace the body with JSON
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
else:
# build response
response = make_response(jsonify({"message": 'Something went wrong'}), 500)
# add the CORS header
response.headers['Access-Control-Allow-Origin'] = '*'
response.content_type = "application/json"
return response
then using Billal's answer:
from flask import Flask
from flask_cors import CORS
# import error handling file from where you have defined it
from . import errors
app = Flask(__name__)
CORS(app) # This will enable CORS for all routes
errors.init_handler(app) # initialise error handling
Try the following decorators:
#app.route('/email/',methods=['POST', 'OPTIONS']) #Added 'Options'
#crossdomain(origin='*') #Added
def hello_world():
name=request.form['name']
email=request.form['email']
phone=request.form['phone']
description=request.form['description']
mandrill.send_email(
from_email=email,
from_name=name,
to=[{'email': app.config['QOLD_SUPPORT_EMAIL']}],
text="Phone="+phone+"\n\n"+description
)
return '200 OK'
if __name__ == '__main__':
app.run()
This decorator would be created as follows:
from datetime import timedelta
from flask import make_response, request, current_app
from functools import update_wrapper
def crossdomain(origin=None, methods=None, headers=None,
max_age=21600, attach_to_all=True,
automatic_options=True):
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, basestring):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, basestring):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
You can also check out this package Flask-CORS
You'd first need to install flask-cors. You can do this as follows:
pip install flask-cors
Once this is installed, you can use it in your Flask app as follows:
If you need to enable CORS for all routes:
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
If you want to enable CORS only for specific routes, you can pass the resources parameter to the CORS function.
For example,
CORS(app, resources={r"/api/*": {"origins": "*"}})
In this example, This code will enable CORS only for routes that start with /api/ and will allow requests from any origin. You can customize the resources parameter to match your needs.
For more information, please read the documentation
My solution is a wrapper around app.route:
def corsapp_route(path, origin=('127.0.0.1',), **options):
"""
Flask app alias with cors
:return:
"""
def inner(func):
def wrapper(*args, **kwargs):
if request.method == 'OPTIONS':
response = make_response()
response.headers.add("Access-Control-Allow-Origin", ', '.join(origin))
response.headers.add('Access-Control-Allow-Headers', ', '.join(origin))
response.headers.add('Access-Control-Allow-Methods', ', '.join(origin))
return response
else:
result = func(*args, **kwargs)
if 'Access-Control-Allow-Origin' not in result.headers:
result.headers.add("Access-Control-Allow-Origin", ', '.join(origin))
return result
wrapper.__name__ = func.__name__
if 'methods' in options:
if 'OPTIONS' in options['methods']:
return app.route(path, **options)(wrapper)
else:
options['methods'].append('OPTIONS')
return app.route(path, **options)(wrapper)
return wrapper
return inner
#corsapp_route('/', methods=['POST'], origin=['*'])
def hello_world():
...
If you can't find your problem and you're code should work, it may be that your request is just reaching the maximum of time heroku allows you to make a request. Heroku cancels requests if it takes more than 30 seconds.
Reference: https://devcenter.heroku.com/articles/request-timeout

Categories

Resources