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.
Related
I have a flask application and I'm trying to use flask-restplus and blueprints. Unfortunately my api endpoint always returns The requested URL was not found on the server. even though I can see that it exists in the output of app.url_map.
The project is laid out as follows:
- app.py
- api
- __init__.py
- resources.py
app.py
from api import api, api_blueprint
from api.resources import EventListResource, EventResource
app = Flask(__name__)
app.register_blueprint(api_blueprint)
db.init_app(flask_app)
app.run()
api/__init__.py
from flask_restplus import Api
from flask import Blueprint
api_blueprint = Blueprint("api_blueprint", __name__, url_prefix='/api')
api = Api(api_blueprint)
api/resources.py
from flask_restplus import Resource
from flask import Blueprint
from . import api, api_blueprint
#api_blueprint.route('/events')
class EventListResource(Resource):
def get(self):
"stuff"
return items
def post(self):
"stuff"
db.session.commit()
return event, 201
The application starts without issue and I can see that '/api/events' appears in app.url_map so I'm not really sure why the url can't be found. Any help appreciated, thanks!
Flask-RESTPlus provides a way to use almost the same pattern as Flaskās blueprint. The main idea is to split your app into reusable namespaces.
You can do it this way:
app.py
from flask_restplus import Api
from api import api_namespace
app = Flask(__name__)
api = Api(app)
db.init_app(flask_app)
from api import api_namespace
api.add_namespace(api_namespace, path='/api')
app.run()
api/init.py
from flask_restplus import Namespace
api_namespace = Namespace('api_namespace')
api/resources.py
from flask_restplus import Resource
from api import api_namespace
#api_namespace.route('/events')
class EventListResource(Resource):
def get(self):
"stuff"
return items
def post(self):
"stuff"
db.session.commit()
Here is the link to documentation:
https://flask-restplus.readthedocs.io/en/stable/scaling.html
I have a Flask REST API. I use healthcheck.EnvironmentDump to make it easy to dump the environment where my service is running. Is it possible to add the endpoint to the Swagger documentation generated by Restplus?
Example
requirements.txt
flask
flask-restplus
gitpython
healthcheck
app.py
#!/usr/bin/env python
"""Simple Flask/Swagger/REST-API example."""
from flask import Flask
from flask_restplus import Resource, Api
from healthcheck import EnvironmentDump
app = Flask(__name__)
api = Api(app, doc='/doc')
# wrap the flask app and give a environment url
# TODO: Add this to API
envdump = EnvironmentDump(app, "/environment")
#api.route('/version')
class VersionAPI(Resource):
def get(self):
import git
repo = git.Repo(search_parent_directories=True)
sha = repo.head.object.hexsha
return sha
#api.route('/health')
class HealthAPI(Resource):
def get(self):
import datetime
return datetime.datetime.now().isoformat()
if __name__ == "__main__":
app.run(host='0.0.0.0')
EnvironmentDump and HealthCheck will each install their own handlers for the endpoint, so your get() instances within your defined Resources will not be reached. That being said, providing a stubbed resource is sufficient for it to show up in the Swagger doc generated by Flask-RESTPlus:
health = HealthCheck(app, '/healthcheck')
#api.route('/healthcheck')
class HealthCheckResource(Resource):
def get(self):
pass
I have a simple API written with the help of flask-restplus:
from flask import Flask
from flask_restplus import Resource, Api
app = Flask(__name__) # Create a Flask WSGI application
api = Api(app) # Create a Flask-RESTPlus API
#api.route('/hello') # Create a URL route to this resource
class HelloWorld(Resource): # Create a RESTful resource
def get(self): # Create GET endpoint
return {'hello': 'world'}
if __name__ == '__main__':
app.run(debug=True)
When I navigate to loacalhost:5000/ in the browser I get a basic Swagger documentation, but I can't find where do I get a machine readable plain yaml representation of the API, should't it be also generated automatically?
I could not find any information around "Swagger Yaml documentation generation" in the official flask-restplus docs. So, I decided to examine the source code and found that the Swagger class implements the Swagger documentation generation for the API instance.
The Swagger class in the flask-restplus source-code is the Swagger documentation wrapper for an API instance. All methods in this class suggest that the API data is serialized as a JSON dictionary. For instance, consider the as_dict() function of this class which serializes the full Swagger specification as a serializable dict. Take a look at the docstring for this function:
from flask import Flask
from flask_restplus import Resource, Api
from flask_restplus.api import Swagger
app = Flask(__name__)
api = Api(app)
swag = Swagger(api)
print(swag.as_dict.__doc__)
#Output:
Output the specification as a serializable ``dict``.
:returns: the full Swagger specification in a serializable format
:rtype: dict
I maybe wrong but the source code suggests that the API documentation is only returned as JSON which is available at http://localhost:5000/swagger.json by default. I could not find anything for YAML.
But there is a workaround to generate YAML documentation for your API. I used the json and yaml libraries to dump the json response from /swagger.json into YAML and save it to yamldoc.yml. You can invoke this by going to http://localhost:5000/swagger.yml. The full code:
from flask import Flask
from flask_restplus import Resource, Api
from flask_restplus.api import Swagger
import requests
import json, yaml
app = Flask(__name__) # Create a Flask WSGI application
api = Api(app) # Create a Flask-RESTPlus API
#api.route('/hello') # Create a URL route to this resource
class HelloWorld(Resource): # Create a RESTful resource
def get(self):
return {'hello': 'world'}
#api.route('/swagger.yml')
class HelloWorld(Resource):
def get(self):
url = 'http://localhost:5000/swagger.json'
resp = requests.get(url)
data = json.loads(resp.content)
with open('yamldoc.yml', 'w') as yamlf:
yaml.dump(data, yamlf, allow_unicode=True)
return {"message":"Yaml document generated!"}
if __name__ == '__main__':
app.run(debug=True)
I hope this helps.
From #amanb answer I made my api return a yaml file, without making any requests.
According to documentation of flask restplus (or, the most recent fork, flask restx) it is possible to export the Swagger specififcations corresponding to your API using:
from flask import json
from myapp import api
print(json.dumps(api.__schema__))
So, instead of using requests I preferred to use api.__schema__.
As my goal is to provide the file for download at the time of the request, it is necessary to use the send_file function of Flask. In addition, this file can be later deleted from the directory so we can use the after_this_request decorator of Flask to invoke the annotated function that will delete the file. The full code:
import os
import json
import yaml
from flask import Flask, after_this_request, send_file, safe_join, abort
from flask_restplus import Resource, Api
from flask_restplus.api import Swagger
app = Flask(__name__) # Create a Flask WSGI application
api = Api(app) # Create a Flask-RESTPlus API
#api.route('/hello') # Create a URL route to this resource
class HelloWorld(Resource): # Create a RESTful resource
def get(self):
return {'hello': 'world'}
#api.route('/swagger.yml')
class HelloWorld(Resource):
def get(self):
data = json.loads(json.dumps(api.__schema__))
with open('yamldoc.yml', 'w') as yamlf:
yaml.dump(data, yamlf, allow_unicode=True, default_flow_style=False)
file = os.path.abspath(os.getcwd())
try:
#after_this_request
def remove_file(resp):
try:
os.remove(safe_join(file, 'yamldoc.yml'))
except Exception as error:
log.error("Error removing or closing downloaded file handle", error)
return resp
return send_file(safe_join(file, 'yamldoc.yml'), as_attachment=True, attachment_filename='yamldoc.yml', mimetype='application/x-yaml')
except FileExistsError:
abort(404)
if __name__ == '__main__':
app.run(debug=True)
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
I am adding Swagger UI to my Python Flask application using Flasgger. Most common examples on the Internet are for the basic Flask style using #app.route:
from flasgger.utils import swag_from
#app.route('/api/<string:username>')
#swag_from('path/to/external_file.yml')
def get(username):
return jsonify({'username': username})
That works.
In my application however, I am not using #app.route decorators to define the endpoints. I am using flask Blueprints. Like following:
from flask import Flask, Blueprint
from flask_restful import Api, Resource
from flasgger.utils import swag_from
...
class TestResourceClass(Resource):
#swag_from('docs_test_get.yml', endpoint='test')
def get() :
print "This is the get method for GET /1.0/myapi/test endpoint"
app = Flask(__name__)
my_api_blueprint = Blueprint('my_api', __name__)
my_api = Api(my_api_blueprint)
app.register_blueprint(my_api_blueprint, url_prefix='/1.0/myapi/')
my_api.add_resource(TestResourceClass, '/test/'
endpoint='test',
methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
....
As seen above, I used #swag_from decorator on the TestResourceClass.get() method which is bound to the GET method endpoint. I also have the endpoint=test matching in the two places.
But I am not getting anything on the Swagger UI, it is all blank. The docs_test_get.yml file does contain the valid yaml markup to define the swagger spec.
What am I missing? How can I get Flasgger Swagger UI working with Flask Blueprint based setup?
Now there is an example of blueprint app in https://github.com/rochacbruno/flasgger/blob/master/examples/example_blueprint.py
"""
A test to ensure routes from Blueprints are swagged as expected.
"""
from flask import Blueprint, Flask, jsonify
from flasgger import Swagger
from flasgger.utils import swag_from
app = Flask(__name__)
example_blueprint = Blueprint("example_blueprint", __name__)
#example_blueprint.route('/usernames/<username>', methods=['GET', 'POST'])
#swag_from('username_specs.yml', methods=['GET'])
#swag_from('username_specs.yml', methods=['POST'])
def usernames(username):
return jsonify({'username': username})
#example_blueprint.route('/usernames2/<username>', methods=['GET', 'POST'])
def usernames2(username):
"""
This is the summary defined in yaml file
First line is the summary
All following lines until the hyphens is added to description
the format of the first lines until 3 hyphens will be not yaml compliant
but everything below the 3 hyphens should be.
---
tags:
- users
parameters:
- in: path
name: username
type: string
required: true
responses:
200:
description: A single user item
schema:
id: rec_username
properties:
username:
type: string
description: The name of the user
default: 'steve-harris'
"""
return jsonify({'username': username})
app.register_blueprint(example_blueprint)
swag = Swagger(app)
if __name__ == "__main__":
app.run(debug=True)
You just need to add your blueprint's name when you reference endpoint. Blueprints create namespaces. Example below. And useful tip: use app.logger.info(url_for('hello1')) for debugging problems with endpoint - it shows very useful error messages like this Could not build url for endpoint 'hello1'. Did you mean 'api_bp.hello1' instead?.
from flask import Flask, Blueprint, url_for
from flask_restful import Api, Resource
from flasgger import Swagger, swag_from
app = Flask(__name__)
api_blueprint = Blueprint('api_bp', __name__)
api = Api(api_blueprint)
class Hello(Resource):
#swag_from('hello1.yml', endpoint='api_bp.hello1')
#swag_from('hello2.yml', endpoint='api_bp.hello2')
def get(self, user=''):
name = user or 'stranger'
resp = {'message': 'Hello %s!' % name}
return resp
api.add_resource(Hello, '/hello', endpoint='hello1')
api.add_resource(Hello, '/hello/<string:user>', endpoint='hello2')
app.register_blueprint(api_blueprint)
swagger = Swagger(app)
app.run(debug=True)
swag_from function have some error to parse file path.you can use doc string to define api in get() first.
flasgger will parse MethodView() method like get,post.
Seems flasgger does not work or has no proper support for blue print style Flask definitions (yet). I used https://github.com/rantav/flask-restful-swagger which worked great!