Fastapi: starlette.routing.NoMatchFound when including Starlette router into FastAPI app

Created on 12 Mar 2020  路  4Comments  路  Source: tiangolo/fastapi

Describe the bug

I have an FastAPI app object which I want to use to several applications via including them as a routers:

import uvicorn

from fastapi import FastAPI
from api import vlantoggler_api_router
from views import vlantoggler_web_router

app = FastAPI()

app.include_router(vlantoggler_api_router,
                   prefix='/vlantoggler/api')

app.include_router(vlantoggler_web_router,
                   prefix='/vlantoggler')

if __name__ == "__main__":
    uvicorn.run(app, loop='uvloop', log_level='debug')

Then here is my api_router:

vlantoggler_api_router = APIRouter(
    # title='Yandex.Cloud Netinfra API',
    # description='A bundled API for Yandex.Cloud Netinfra team tools',
    routes=[
        APIRoute('/', check, methods=['GET'], tags=['VlanToggler'],
                 name='Get current interface state',
                 # summary='String replaces function name AND name on Swagger API page',
                 description='ToR and Interface names are validated and current interface state is returned',
                 response_description='Successfully got interface state',
                 response_class=JSONResponse,
                 response_model=Success,
                 responses={**get_responses}
                 ),
        APIRoute('/', toggle, methods=['POST'], tags=['VlanToggler'],
                 name='Switch interface state',
                 description='ToR and Interface names are validated and ToR interface is toggled to desired state',
                 response_description='Interface successfully switched',
                 response_class=JSONResponse,
                 response_model=Success,
                 responses={**post_responses},
                 ),
    ],
)

and web form router

vlantoggler_web_router = Router(
    [
        Route('/', home, methods=['GET', 'POST']),
        Mount('/statics', statics, name='static'),
    ],
)

As you can see I just import them from another module to make things nice and clean.

To Reproduce

When I run the app:

python3 ft.py
INFO:     Started server process [14057]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

My api works perfectly fine and accessable via http://127.0.0.1:8000/vlantoggler/api/.

There is also nothing wrong with the /docs, they are at http://127.0.0.1:8000/docs as I want it.

BUT web form doesn't work (should be accessable via http://127.0.0.1:8000/vlantoggler/). I have an Internal Server Error instead:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/uvicorn/protocols/http/httptools_impl.py", line 385, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/usr/local/lib/python3.6/dist-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.6/dist-packages/fastapi/applications.py", line 151, in __call__
    await super().__call__(scope, receive, send)  # pragma: no cover
  File "/usr/local/lib/python3.6/dist-packages/starlette/applications.py", line 102, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.6/dist-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/usr/local/lib/python3.6/dist-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.6/dist-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/usr/local/lib/python3.6/dist-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.6/dist-packages/starlette/routing.py", line 550, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.6/dist-packages/starlette/routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.6/dist-packages/starlette/routing.py", line 41, in app
    response = await func(request)
  File "/home/horseinthesky/vl_proto/views.py", line 83, in home
    return render(request)
  File "/home/horseinthesky/vl_proto/views.py", line 40, in render
    return templates.TemplateResponse('index.html', context)
  File "/usr/local/lib/python3.6/dist-packages/starlette/templating.py", line 87, in TemplateResponse
    background=background,
  File "/usr/local/lib/python3.6/dist-packages/starlette/templating.py", line 27, in __init__
    content = template.render(context)
  File "/usr/local/lib/python3.6/dist-packages/jinja2/asyncsupport.py", line 76, in render
    return original_render(self, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/local/lib/python3.6/dist-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.6/dist-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "templates/index.html", line 10, in top-level template code
    <link href="{{ url_for('static', path='/css/bootstrap.min.css') }}" rel="stylesheet">
  File "/usr/local/lib/python3.6/dist-packages/starlette/templating.py", line 59, in url_for
    return request.url_for(name, **path_params)
  File "/usr/local/lib/python3.6/dist-packages/starlette/requests.py", line 137, in url_for
    url_path = router.url_path_for(name, **path_params)
  File "/usr/local/lib/python3.6/dist-packages/starlette/routing.py", line 486, in url_path_for
    raise NoMatchFound()
starlette.routing.NoMatchFound

Expected behavior

Web router should be working on http://127.0.0.1:8000/vlantoggler/

Environment

  • OS: Ubuntu 18.04.03
  • FastAPI Version 0.52.0, get it with: pip
  • Starlette Version 0.13.2, get it with pip
python3 -c "import fastapi; print(fastapi.__version__)"
0.52.0
  • Python version 3.6.7, get it with System
python3 --version                                      
Python 3.6.7
answered question

All 4 comments

Check the note with the "Very Technical Details" in this section: https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-prefix-tags-responses-and-dependencies

You can use include_router for FastAPI APIRouters, but not to mount a sub-application. If you need to do that, use a mount.

@tiangolo Thank you very much.
I changed

from starlette.routing import Router

vlantoggler_web_router = Router(
    [
        Route('/', home, methods=['GET', 'POST']),
        Mount('/statics', statics, name='static'),
    ],
)

to

from starlette.applications import Starlette

vlantoggler_web_app = Starlette(
    routes=[
        Route('/', home, methods=['GET', 'POST']),
        Mount('/statics', statics, name='static'),
    ],
)

And then included APIRouters with include_router and

from fastapi import FastAPI
from api import vlantoggler_api_router
from views import vlantoggler_web_app

app = FastAPI(
    title='Yandex.Cloud Netinfra API',
    description='A bundled API for Yandex.Cloud Netinfra team tools',
)
app.include_router(vlantoggler_api_router,
                   prefix='/vlantoggler/api')
app.mount('/vlantoggler', vlantoggler_web_app)

And this worked perfectly fine.

Just want to clarify: Router from starlette.routing is not similar object to APIRouter from fastapi.routing? And I cannot include_router a Router object to FASTApi app object?!

So Router is more like another FASTApi object and I can only mount one to another?

Yes, the APIRouter class inherits from Router, and APIRouter is what adds all the FastAPI stuff.

include_router doesn't "mount" an app, it creates a _path operation_ for each _path operation_ in the "included" router. That's what allows it to be in the same OpenAPI, the same app, the same docs, etc.

Check the details in the technical notes here: https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter

Ok. Thank you for your help.

Was this page helpful?
0 / 5 - 0 ratings