python-restx / flask-restx

Fork of Flask-RESTPlus: Fully featured framework for fast, easy and documented API development with Flask
https://flask-restx.readthedocs.io/en/latest/
Other
2.16k stars 335 forks source link

"Could not build url for endpoint" when using nested blueprints #517

Open ghost opened 1 year ago

ghost commented 1 year ago

Code

from myapi import api_v1

# Create the blueprints
api_blueprint = Blueprint("api", __name__, url_prefix="/api")
api_v1_blueprint = Blueprint("api_v1", __name__, url_prefix="/v1")

# Register the nested blueprint
api_blueprint.register_blueprint(api_v1_blueprint)

# Add the API to the nested blueprint
api_v1.init_app(api_v1_blueprint)

# Finally, register the parent blueprint to the app
app.register_blueprint(api_blueprint)

Repro Steps (if applicable)

  1. Go to the /api/v1 page

Expected Behavior

The interactive swagger documentation for api_v1 is shown

Actual Behavior

The exception Could not build url for endpoint 'api_v1.specs'. Did you mean 'api.api_v1.specs' instead? is raised

Error Messages/Stack Trace

127.0.0.1 - - [12/Feb/2023 20:20:32] "GET /api/v1/ HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/usr/lib/python3.10/site-packages/flask/app.py", line 2548, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/lib/python3.10/site-packages/flask/app.py", line 2528, in wsgi_app
    response = self.handle_exception(e)
  File "/home/matthieu/.local/lib/python3.10/site-packages/flask_restx/api.py", line 674, in error_router
    return original_handler(e)
  File "/usr/lib/python3.10/site-packages/flask/app.py", line 2525, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/lib/python3.10/site-packages/flask/app.py", line 1822, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/matthieu/.local/lib/python3.10/site-packages/flask_restx/api.py", line 674, in error_router
    return original_handler(e)
  File "/usr/lib/python3.10/site-packages/flask/app.py", line 1820, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/lib/python3.10/site-packages/flask/app.py", line 1796, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "/home/matthieu/.local/lib/python3.10/site-packages/flask_restx/api.py", line 456, in render_doc
    return apidoc.ui_for(self)
  File "/home/matthieu/.local/lib/python3.10/site-packages/flask_restx/apidoc.py", line 35, in ui_for
    return render_template("swagger-ui.html", title=api.title, specs_url=api.specs_url)
  File "/home/matthieu/.local/lib/python3.10/site-packages/flask_restx/api.py", line 542, in specs_url
    return url_for(
  File "/usr/lib/python3.10/site-packages/flask/helpers.py", line 256, in url_for
    return current_app.url_for(
  File "/usr/lib/python3.10/site-packages/flask/app.py", line 2031, in url_for
    return self.handle_url_build_error(error, endpoint, values)
  File "/usr/lib/python3.10/site-packages/flask/app.py", line 2020, in url_for
    rv = url_adapter.build(  # type: ignore[union-attr]
  File "/usr/lib/python3.10/site-packages/werkzeug/routing/map.py", line 917, in build
    raise BuildError(endpoint, values, method, self)
werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'api_v1.specs'. Did you mean 'api.api_v1.specs' instead?

Environment

Additional Context

This is the code used to define api_v1

api_v1 = Api(
    version="1.0",
    title="Test API",
    description="An API to test flask_restx",
    endpoint="/api/v1",
)
peter-doggart commented 1 year ago

Flask-restx was written before Flask >2.0.0 introduced nested blueprints and therefore the Api class makes assumptions about the URLs and endpoints inside a Blueprint. I can't see any easy work around either.

Can I ask why you are interested in using nested blueprints rather than just registering another top level blueprint or using namespaces?

ghost commented 1 year ago

I'm interested in using nested blueprints since it seemed like the easiest way (mostly easy to register, as well as easy to add a redirect from /api to the latest API version).

As for namespaces, i am using them already to register various things to api_v1, but i don't really see how i could use it to have multiple different versions on the API with different prefix.

But if there isn't an easy way to fix it to work with nested blueprints, i'll follow your suggestion and use multiple top-level blueprints instead

Wankupi commented 1 month ago

I meet this problem today. It would be an useful change to support nested blueprint for me.  The problem I meet is:

Thanks for consideration!