Fastapi: [QUESTION] How to get the api path in middleware?

Created on 30 Aug 2019  Â·  6Comments  Â·  Source: tiangolo/fastapi

Description
I want to store some analytics data for each hit in my API.
Among the data I need to extract for each request, I have:

  • response status code (eg 200)
  • the request's matched path (eg /users/{user_id}/books)
  • etc.

I imagined that the best place to do that is in a middleware, after the response is computed:

@app.middleware("http")
async def get_analytics_data(request: Request, call_next):
    response = await call_next(request)
    analytics_data = {
        'response_code': response.status_code,
        'path': ?  # here I need the route path (eg `/users/{user_id}/books`)
    }
    save(analytics_data)
    return response

Inside the middleware I can access the request.url but it's not what I want. The path as I define it in the routes is what I need.

Is there a way to obtain the path of the request in a single place? It doesn't have to happen at the middleware level as long as it is in a single place.

EDIT:

Example:

Let's say I have defined:

@router.get('/users/{user_id}/books')
async def get_user_books(user_id: str):
    return [{book1}, {book2}]
  1. The user hits /users/10/books, so the path /users/{user_id}/books is matched.
  2. In my middleware I need to identify what path had been matched (which, in this case, it's /users/{user_id}/books).
question

All 6 comments

app.url_path_for('get_analytics_data') maybe?

Le ven. 30 août 2019 à 3:48 PM, devtud notifications@github.com a écrit :

Description
I want to store some analytics data for the hits in my API, and, among the
data I need to extract for each request, I have: response status code (eg
200), the resolved path (eg /users/{user_id}/books) etc.

I imagined that the best place to do that is in a middleware, after the
response is computed:

@app.middleware("http")async def get_analytics_data(request: Request, call_next):
response = await call_next(request)
analytics_data = {
'response_code': response.status_code,
'path': ?
}
save(analytics_data)
return response

Inside the middleware I can access the request.url but it's not what I
want. The path as I define it in the routes is what I need.

Is there a way to obtain the path of the request in a single place? It
doesn't have to happen at the middleware level as long as it is in a single
place.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/tiangolo/fastapi/issues/486?email_source=notifications&email_token=AAINSPV2EJLDM5LX6MRFASLQHEQK7A5CNFSM4ISNQZIKYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4HIOSC7A,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAINSPX55YURAWAR6JSJ2BDQHEQK7ANCNFSM4ISNQZIA
.

@euri10 , get_analytics_data is the name of the middleware, so I don't see how could I obtain the route's path from it.

I've slightly updated the description hoping it's a bit more clear.

A temporary (or not) solution for my need implies using router and endpoint from request.scope. I'm not sure this is an elegant way to do it, but it does the job. Other suggestions are welcomed.

So, to get the matched path of a request, I use this snippet in my middleware.

...
response = await call_next(request)

path = [route for route in request.scope['router'].routes if route.endpoint == request.scope['endpoint']][0].path

print(f'Path is: {path}')
# Path is: /users/{user_id}/books

return response

The downside is that I have to iterate through all the routes for each request, but this can be improved quickly with a cache global variable.

I'm glad you solved your problem @devtud !

You might also want to check how to add custom classes for APIRoute: https://fastapi.tiangolo.com/advanced/custom-request-and-route/

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

Is there a way to get the path before the response = await call_next(request) ?

...
response = await call_next(request)

path = [route for route in request.scope['router'].routes if route.endpoint == request.scope['endpoint']][0].path

print(f'Path is: {path}')
# Path is: /users/{user_id}/books

return response
Was this page helpful?
0 / 5 - 0 ratings