-5

I have my FastAPI app define in server.py

app = FastAPI(
debug=True, title="Microservice for APIs",
description="REST APIs",
version="0.0.1",
openapi_url="/v3/api-docs",
middleware=[
    Middleware(AuthorizationMiddleware, authorizor=Auth())
]) 

In __init__.py, I have routes defined

from fastapi import APIRouter
api_router = APIRouter()
api_router.include_router(impl_controller.router, prefix="/impl/data",
                      tags=["APIs for Impl Management"])

In impl_controller.py, I have define routes like this

@router.get('{id}/get_all')
def hello_world():
    return {"msg": "Hello World"}

@router.get('{id}/get_last')
def test():
    return {"msg": "test"}

In the middleware, I'm trying to get request route and not the URL

def check_for_api_access(self, request: Request):
    request_path = request.scope['path']
    # route_path = request.scope['endpoint']  # This does not exists

    url_list = [
        {'path': route.path, 'name': route.name}
        for route in request.app.routes
    ]

Result I'm expecting is: {id}/get_all for 1st request and {id}/get_last for 2nd request.

I'm able to get list of all paths in url_list but I want route path for the specific request

Tried solution provided here: https://github.com/tiangolo/fastapi/issues/486 that also not working for me

Darshan
  • 352
  • 8
  • 24
  • I think when you define your paths in the router decorator they should all start with `"/"`. – NeilG Jan 16 '23 at 05:39
  • Future readers might find [this](https://stackoverflow.com/a/72239186/17865804) and [this](https://stackoverflow.com/a/75729917/17865804), as well as [this](https://stackoverflow.com/a/74262037/17865804) and [this](https://stackoverflow.com/a/70904297/17865804) helpful. – Chris May 09 '23 at 02:54

3 Answers3

0

Try this:

def check_for_api_access(self, request: Request):
   api_route = next(item for item in request.app.routes if isinstance(item, APIRoute) and item.dependant.cache_key[0] == request.scope['endpoint'])
   print(api_route.path)
RKO
  • 120
  • 9
0

With hacking into the startlette framework:

def get_router_path(request: Request) -> Optional[str]:
    current_path = None
    for route in request.app.routes:
        if route.matches(request.scope):
            match, _ = route.matches(request.scope)
            if match == Match.FULL:
                return route.path
            elif match == Match.PARTIAL and current_path is None:
                current_path = route.path

    return current_path
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 09 '23 at 15:04
-1

You may not be able to accomplish exactly what you need, though you can get very close with the standard framework (i.e. no fancy alterations of the framework).

In the middleware, you can access the request directly. This way, you'll be able to inspect the requested url, as documented in https://fastapi.tiangolo.com/advanced/using-request-directly/?h=request . The attributes that are accessible are described here https://www.starlette.io/requests/


N.B. Since you posted just snippets, it's very difficult to say where values/variables come from.

In your case, it's dead simple what is not working. If you looked at the urls I posted, the starlette docs, shows the attributes that you can access from a request. This contains exactly the attribute you are looking for.

Basically, change request_path = request.scope['path'] into request_path = request.url.path. If you have a prefix, then you'll also get that and that's why I said You may not be able to accomplish exactly what you need, though you can get very close with the standard framework (i.e. no fancy alterations of the framework). Still, if you know your prefix, you can remove it from the path (it's just a string).

E_net4
  • 27,810
  • 13
  • 101
  • 139
lsabi
  • 3,641
  • 1
  • 14
  • 26
  • I went through these links. I didn't find anything helpful which will resolve my issue. Can you please specify a code snippet instead which is relevant to my question? – Darshan May 19 '21 at 05:22
  • `request.url.path` this gives me `111/get_all`, `111/get_last`. I don't want the ID here, instead `{id}/get_all`, `{id}/get_last` which I have mentioned clearly in the question – Darshan May 19 '21 at 10:43
  • As I said, you cannot do it without hacking the framework itself. Also, this part is not specified in the question, just as a general example, so I couldn't be aware of that. Though, you could match the string pattern with a regex and substitute the id part with the "{id}". – lsabi May 19 '21 at 12:06
  • @Darshan I don't understand the reason for the downvote. I clearly stated in my answer that you could get close to what you wanted, though not perfectly match the request without hacking. I proposed a solution that comes close and could even achieve what you're looking for (use regex to replace part you don't care about). In your question you didn't specify clearly what you needed (i.e. literally `/{id}/get_all/`) so I provided an answer, which satisfies your request. I can understand not getting the correct answer vote, but not the downvote. Do you mind elaborating? – lsabi May 22 '21 at 12:58
  • I don't see any mention of `route_path` in your question.... but feel free to hack the framework and then post the answer with details on how you did it. Now I'm curious – lsabi May 24 '21 at 12:14