flask-smorest
flask-smorest copied to clipboard
Add url_prefix for each blueprint
Hello,
I'm considering using flask-smorest for our api. I especially like the interaction with the marshmallow library and the rather simple handling of flask-smorest. Nevertheless, while going through this library I noticed one thing: Since flask-smorest seems to be based on creating blueprints that are added to an app/api, I don't have the possibility to create a blueprint with a fixed url_prefix and assign many possible namespaces to that blueprint (as in flask-restx). Here in flask-smorest the blueprints are a kind of namespace under which I define different routes with endpoints. But I would like to have the possibility to define a url_prefix for all blueprints without having to write it into every blueprint by hand. How do I do this as effective as possible so that I have to modify as little internal code of the library as possible? How do I also get the url_prefix not to be displayed in swagger ui for each endpoint?
I don't want to add all blueprints to an app, but to a kind of layer that defines a prefix for all blueprints and then add this layer to the app. Furthermore, the prefix of this layer should not appear in every route in swagger, but should be present as a kind of base url for every route.
Many greetings
I suppose you could just override Blueprint
to change the init to pass your prefix. But it would probably appear in the spec.
I'm not sure I fully understand what you're trying to achieve. I generally expose my APIs under a subpath like https://domain.tld/myapi/v1.0/
. I do it at webserver level, in my apache config file.
WSGIScriptAlias /myapi/v1.0/ /path/to/application.wsgi
I hope this helps.
How to achieve this in flask level?
putting /v1/
in every blueprint is not maintainable / hard to change
blp = Blueprint(
"Health", __name__, url_prefix="/v1/health", description="Health operations"
)
tried passing base_prefix
to register_blueprint
but didn't work either
def register_blueprints(api):
"""Initialize application with all modules"""
for module in MODULES:
api.register_blueprint(module.blp, base_prefix="/v2")
Tried spec_kwargs={"basePath": "/v2"}
and I get A name collision occurred between blueprints
def register_blueprints(app):
"""Register Flask blueprints."""
api = extensions.create_api(app)
api.init_app(app, spec_kwargs={"basePath": "/v2"})
modules.register_blueprints(api)
Hi,
I don't know if it's the "official" way to do it, but I'm adding a common url prefix as follows:
def register_blueprints(api):
"""Initialize application with all modules"""
for module in MODULES:
api.register_blueprint(module.bp, url_prefix=f"/{common_prefix}/{module.bp.url_prefix}")
solution
My current solution is to skip url_prefix
in each resources, and changing from @blp.route("/")
to @blp.route("/health")
. then assigning url_prefix
when registering bluprint
blp = Blueprint("Health", __name__, description="Health operations")
@blp.route("/health")
class HealthCheck(MethodView):
@blp.response(HealthSchema)
def get(self):
...
def register_blueprints(api):
"""Initialize application with all modules"""
for module in MODULES:
api.register_blueprint(module.blp, url_prefix="/v2/")
I have write this reply 9 hours ago, but forgot to click "comment" :)
Btw, I see your approach @svidela is simpler.
class MyApi(Api):
def register_blueprint(self, blp, **options):
"""Register a blueprint in the application
Also registers documentation for the blueprint/resource
:param Blueprint blp: Blueprint to register
:param options: Keyword arguments overriding Blueprint defaults
Must be called after app is initialized.
"""
blp.url_prefix = f'{self._app.config.get("APPLICATION_ROOT")}{blp.url_prefix}'
blp_name = blp.name if "name" not in options else options["name"]
self._app.register_blueprint(blp, **options)
# Register views in API documentation for this resource
blp.register_views_in_doc(self, self._app, self.spec, name=blp_name)
# Add tag relative to this resource to the global tag list
self.spec.tag({"name": blp_name, "description": blp.description})