Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

route_class not respected in nested router when trying to use custom APIRoute class #4110

Closed
9 tasks done
jgould22 opened this issue Oct 29, 2021 · 2 comments
Closed
9 tasks done
Labels
question Question or problem question-migrate

Comments

@jgould22
Copy link

jgould22 commented Oct 29, 2021

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google "How to X in FastAPI" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

The below code is a small working example

import time
from typing import Callable

from fastapi import APIRouter, FastAPI, Request, Response
from fastapi.routing import APIRoute


class TimedRoute(APIRoute):
    def get_route_handler(self) -> Callable:
        original_route_handler = super().get_route_handler()

        async def custom_route_handler(request: Request) -> Response:
            before = time.time()
            response: Response = await original_route_handler(request)
            duration = time.time() - before
            response.headers["X-Response-Time"] = str(duration)
            print(f"route duration: {duration}")
            print(f"route response: {response}")
            print(f"route response headers: {response.headers}")
            return response

        return custom_route_handler


app = FastAPI()
router = APIRouter(route_class=TimedRoute)

@app.get("/")
async def not_timed():
    return {"message": "Not timed"}


@router.get("/timed")
async def timed():
    return {"message": "It's the time of my life"}


app.include_router(router)

The above works and the router is timed correctly

The below does not and the route is not timed

import time
from typing import Callable

from fastapi import APIRouter, FastAPI, Request, Response
from fastapi.routing import APIRoute


class TimedRoute(APIRoute):
    def get_route_handler(self) -> Callable:
        original_route_handler = super().get_route_handler()

        async def custom_route_handler(request: Request) -> Response:
            before = time.time()
            response: Response = await original_route_handler(request)
            duration = time.time() - before
            response.headers["X-Response-Time"] = str(duration)
            print(f"route duration: {duration}")
            print(f"route response: {response}")
            print(f"route response headers: {response.headers}")
            return response

        return custom_route_handler


router_1 = APIRouter()

@router_1.get("/")
async def not_timed():
    return {"message": "Not timed"}


@router_1.get("/timed")
async def timed():
    return {"message": "It's the time of my life"}

# The route is no longer timed
router = APIRouter(route_class=TimedRoute)
router.include_router(router_1)

# This also doesnt work 
# router = APIRouter()
# router.include_router(router_1)
# router.route_class = TimedRoute

app = FastAPI()
app.include_router(router)

Description

Trying use a a router with a custom APIRoute class I have included a small example based on the documentation at https://fastapi.tiangolo.com/advanced/custom-request-and-route/?h=

  • Run sample code with uvicorn
  • Make a request to /timed
  • Get {"message": "It's the time of my life"} when requesting but not get the extra header, nor are the lines logged

This may or may not be related to the below issues, they were not very clear

Operating System

Linux

Operating System Details

[jordan@box]$ lsb_release -a
LSB Version:    1.4
Distributor ID: EndeavourOS
Description:    EndeavourOS Linux
Release:        rolling
Codename:       rolling
[jordan@box]$ uname -a
Linux box 5.14.14-arch1-1 #1 SMP PREEMPT Wed, 20 Oct 2021 21:35:18 +0000 x86_64 GNU/Linux

FastAPI Version

fastapi==0.70.0

Python Version

Python 3.9.7

Additional Context

No response

@jgould22 jgould22 added the question Question or problem label Oct 29, 2021
@gamgi
Copy link

gamgi commented Nov 9, 2021

@jgould22 I tried to reproduce the issue in a notebook here with slight changes: tinnable.com/tins/13c1743f-07c6-4260-9fd8-1f9a7f2b91d7. With the changes, it does not reproduce.

It seems that the issue is fixed by changing

- router_1 = APIRouter()
+ router_1 = APIRouter(route_class=TimedRoute)

It is not enough that the parent router has the route_class. The subrouter router_1 with the endpoint definition also has to be of the custom class. This is not mentioned in the docs, but seems at least initially reasonable, since it is not clear how nested route classes should be propagated.

@jgould22
Copy link
Author

Yeah. I know you can define a custom_route_handler for all the routers and it works.

I was hoping I could do it for only a subset of my routes.

The docs imply in some places you can treat the APIRouter as a mini FastAPI class but I guess this has some exceptions.

I will try instead to use a sub-application to split things instead of a router.

Thanks for your help.

@tiangolo tiangolo reopened this Feb 27, 2023
@fastapi fastapi locked and limited conversation to collaborators Feb 27, 2023
@tiangolo tiangolo converted this issue into discussion #6430 Feb 27, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
question Question or problem question-migrate
Projects
None yet
Development

No branches or pull requests

3 participants