as i know so far there are two ways of binding resouces to an endpoint using flask framework,
the first one is using the #app.route decorator, like this:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
the second way is to create a class that inherite from Resources in flask-restfull, this class contains the http methods as functions, we bind it to an endpoint using add_resource method, as follow:
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
return 'Hello, World!'
api.add_resource(HelloWorld, '/')
if __name__ == '__main__':
app.run()
as I understand this two different syntaxes do the same thing, what I dont understand is what is the difference between the two ? or is one designed for a specific type of applications and the other to another type of applications ?
Flask-RESTful is an extension of Flask, which itself is built on many of the excellent utilities provided by Werkzeug.
from flask import Flask
app = Flask(__name__)
#app.route('/foo')
def say_foo():
return 'foo'
#app.route('/bar')
def say_bar():
return 'bar'
if __name__ == '__main__':
app.run()
One of the biggest ideas behind REST is using HTTP to interact with resources. The problem with this code is our resource is split up over multiple methods. There's no encapsulation. While the API itself incorporates basic elements of REST, the code completely fails to capture these ideas. This is bad! There's no reason our internal code shouldn't match the external appearance of our API.
Using Flask-RESTful
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class Foo(Resource):
def get(self):
return 'foo'
class Bar(Resource)
def get(self):
return 'bar'
# As you might have guessed, these two lines add a given resource to our API at the
# specified route. We no longer need to enumerate what methods a route supports,
# since Flask-RESTful resolves this information by inspecting what methods you've
# defined on your resource object.
api.add_resource(Foo, '/foo')
api.add_resource(Bar, '/bar')
if __name__ == '__main__':
app.run()
We have classes now! This is a huge deal. Our routes now map directly to objects. Even better, the methods on a given class are exactly the same as their HTTP counterparts. We no longer have to deal with naming methods on our routes like say_foo, since there's a 1 to 1 mapping between HTTP methods and methods on our classes.
read more: https://dougblack.io/words/flask-restful-101.html
Related
How to check what route has been used?
Using #api with Flask-restful and Python at the moment I'm not doing it in a clean way by checking api.endpoint value.
How do I do it correctly?
#api.route('/form', endpoint='form')
#api.route('/data', endpoint='data')
class Foobar(Resource):
def post(self):
if api.endpoint == 'api.form':
print('form')
elif api.endpoint == 'api.data':
print('data')
EDIT:
Should I split it into two classes?
I am in no way a professional with flask so please do take my answer with a grain of salt. First of all I would definitely split it into 2 different classes just to have a better overview of what you are doing. Also as a rule of thumb I would always split the apis and write its own logic for a higher degree of granularity.
Second if you want to have a look at https://flask-restful.readthedocs.io/en/latest/api.html#flask_restful.Api.owns_endpoint. This might be of assistance for you.
I am new with python and flask.
I think something like the following should work for you:
from flask import Flask, request
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
class Data(Resource):
def post(self):
print("data")
return{"type": "data"}
class Form(Resource):
def post(self):
print("form")
return{"type": "form"}
api.add_resource(Form, '/form')
api.add_resource(Data, '/data')
if __name__ == "__main__":
app.run(port=8080)
Also you have use seperate files for the classes for a cleaner code like:
form.py
from flask_restful import Resource
class Form(Resource):
def post(self):
print("form")
return{"type": "form"}
data.py
from flask_restful import Resource
class Data(Resource):
def post(self):
print("data")
return{"type": "data"}
services.py
from flask import Flask, request
from flask_restful import Api
from data import Data
from form import Form
app = Flask(__name__)
api = Api(app)
api.add_resource(Form, '/form')
api.add_resource(Data, '/data')
if __name__ == "__main__":
app.run(port=8080)
Hope this helps.
I'm writing a flask application.
It makes sense to have multiple endpoints, like that:
prefix + '/'
prefix + '/<id>'
prefix + '/<id>/parameters'
prefix + '/<id>/parameters/<param>'
However, if I try to declare them all inside a blueprint, I'm getting a AssertionError: Handler is overwriting existing for endpoint _blueprintname_._firsthandlername_
Is there any way around this? I know it's been straight-forwardly done before, in technologies like .net core. Thanks in advance.
If you plan to add many parameters in your routes, you could have a look at this module for flask.
It helps you assigning routes to resources.
You could build up a set of routes as follows:
from flask import Flask, request
from flask_restful import Resource, Api, reqparse
app = Flask(__name__)
api = Api(app)
class Some(Resource):
def get(self, id=None, params=None):
"""
In order to allow empty params for routes, the named arguments
must be initialized
"""
if id and params:
return {'message':'this is get with id and params'}
if id:
return {'message':'this is get with id'}
return {'message':'this is get'}
def post():
"""
One can use here reqparse module to validate post params
"""
return {'message':'this is post'}
# Add the resource to the service
api.add_resource(Some, '/', '/<string:id>','/<string:id>/<string:params>', ...)
# start the service
if __name__ == '__main__':
app.run(debug=True)
I am building a Flask app with a blueprint mounted on two different endpoint (one is a legacy alias to the other).
In my blueprint class:
ldp = Blueprint('ldp', __name__)
#ldp.route('/<path:uuid>', methods=['GET'])
#ldp.route('/', defaults={'uuid': None}, methods=['GET'],
strict_slashes=False)
def get_resource(uuid):
# Route code...
In my main server code:
app = Flask(__name__)
app.config.update(config['flask'])
app.register_blueprint(ldp, url_prefix='/new_ep')
# Legacy endpoint. #TODO Deprecate.
app.register_blueprint(ldp, url_prefix='/old_ep')
How can I get the actual URL of the request up to the /old_ep or /new_ep part in the route method, e.g. http://localhost:5000/new_ep?
So far I have used
request.host_url + request.path.split('/')[1]
but it looks quite inelegant and possibly error-prone. I would like to use the information from the blueprint setup if possible.
Thanks for your help.
EDIT: I could get to the Blueprint instance from within the request with
current_app.blueprints[request.blueprint]
and I was hoping that the url_prefix attribute that I set when registering the blueprint was there, but it is None instead. As I read from the documentation for the supposedly related iter_blueprints() method, apparently these blueprints are listed without regard of how many times and with which parameters they were registered. Too bad.
Here is a full working example to get the idea based off issue 612
from flask import Flask, Blueprint, url_for, request, g
bp = Blueprint('whatever', __name__)
#bp.url_defaults
def bp_url_defaults(endpoint, values):
url_prefix = getattr(g, 'url_prefix', None)
if url_prefix is not None:
values.setdefault('url_prefix', url_prefix)
#bp.url_value_preprocessor
def bp_url_value_preprocessor(endpoint, values):
g.url_prefix = values.pop('url_prefix')
#bp.route('/something')
def index():
return 'host prefix is %s%s' % (request.host_url, g.url_prefix)
app = Flask(__name__)
app.register_blueprint(bp, url_prefix='/new_ep', url_defaults={'url_prefix': 'new_ep'})
app.register_blueprint(bp, url_prefix='/old_ep', url_defaults={'url_prefix': 'old_ep'})
Say I have enabled authentication to the resources using BasicAuth:
class MyBasicAuth(BasicAuth):
def check_auth(self,username,password,allowed_roles,resource,method):
return username == 'secretusername' and password == 'secretpass'
I also have custom routes which are used to manage documents from a HTML view. How do I use the same MyBasicAuth to protect the all the custom routes? I also need to implement logic which authenticates using the above MyBasicAuth.
Please help me with this. It's for personal use, so I preferred hard coding the username and password.
If you are trying to use a custom end-point Authentication you will find it difficult as mentioned here:
https://github.com/pyeve/eve/issues/860
I ended up writing a wrapper to get around the issue of 'resource' not being passed to 'requires_auth':
def auth_resource(resource):
def fdec(f):
#wraps(f)
def wrapped(*args, **kwargs):
return f(resource=resource, *args, **kwargs)
return wrapped
return fdec
This way you can define in your DOMAIN an authentication class:
DOMAIN = {
'testendpoint'= {'authentication':MyCustomAuthetication},
'otherendpoints'=...
And in my app I have wrapped the requires_auth decorator and added this as a authentication resource.
#app.route('/testendpoint/<item>', methods=['GET'])
#auth_resource('testendpoint')
#requires_auth('item')
def my_end_point_function(*args, **kwargs):
dosomthinghere
As long as an authentication class is defined in the settings file for an endpoint, this also allows you to reuse any authentication defined in another endpoint which may be handy if you want to make sure all the endpoints use the same authentication.
You can leverage the requires_auth decorator which is used internally by Eve itself. That way, your auth class will also be used to protect your custom routes:
from eve import Eve
from eve.auth import requires_auth
app = Eve()
#app.route('/hello')
#requires_auth('resource')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
If you are using flask blueprints for your custom routes, you can add a before request function for your blueprint to do that.
First, create a function to check authentication from blueprints. You need to get the Authorization header from the flask request by yourself, like this:
from flask import request, abort, current_app
from werkzeug.http import parse_authorization_header
def check_blueprint_auth():
if 'Authorization' not in request.headers:
print('Authorization header not found for authentication')
return abort(401, 'Authorization header not found for authentication')
header = parse_authorization_header(request.headers['Authorization'])
username = None if header is None else header['username']
password = None if header is None else header['password']
return username == 'secretusername' and password == 'secretpass'
Then, you can set this function to be called before each blueprint's request. Below is an example of a blueprint definition, setting the before_request function:
from flask import Blueprint, current_app as app
# your auth function
from auth import check_blueprint_auth
blueprint = Blueprint('prefix_uri', __name__)
# this sets the auth function to be called
blueprint.before_request(check_blueprint_auth)
#blueprint.route('/custom_route/<some_value>', methods=['POST'])
def post_something(some_value):
# something
Finally, you need to bind the blueprint with your eve app. An example on how to bind blueprints, taken in part from here:
from eve import Eve
# your blueprint
from users import blueprint
from flask import current_app, request
app = Eve()
# register the blueprint to the main Eve application
app.register_blueprint(blueprint)
app.run()
Hope that helps.
I inherited a project that uses flask. This flask application has several APIs, each API has a GET function that returns a json object.
I was asked to implement an additional API that requests information from the other APIs. So my question is how do I make a GET request directly to a flask application? Is it something like....
from flask import request
#app.route('/root_dir/api_number_1/info', methods=['GET'])
def request_info_from_api_number_1():
return request.get_json()
#app.route('/root_dir/api_number_2/info', methods=['GET'])
def request_info_from_api_number_2():
return request.get_json()
When I do this the functions return None. I suppose I could always make an http request to the flask url and specify the address as localhost but it seems strange to have to make an http request when I can directly access the flask app object.
Could you just use the existing api functions?
from flask import Flask
app = Flask(__name__)
#app.route("/foo")
def foo():
return "foo"
#app.route("/foobar")
def foobar():
return foo() + "bar"
if __name__ == "__main__":
app.run(debug=True)