오늘은 fastapi의 middleware에 대해서 알아보려고 합니다.
https://fastapi.tiangolo.com/tutorial/middleware/
fastapi의 middleware를 활용하면, 모든 request에 대해서 전/후 처리를 추가해줄 수 있습니다.
바로 예시 코드를 보도록 하겠습니다.
import time
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
위와 같이, @app.middleware("http") 라는 데코레이터를 활용하여 함수를 위와 같이 선언하면, 자동으로 middleware에 등록되게 됩니다.
위의 예시에서는 response의 header에 각 request의 실행 시간을 함께 반환하게 됩니다.
보통 백엔드 서버를 개발하게 되면, 모든 request에 대해서 로그를 남기는 작업을 middleware를 활용해서 구현할 수 있는데요.
Request 객체에 request에 대한 정보들도 들어있어서, 어떤 request인지와 헤더 정보 등등을 middleware에서 로깅하도록 구현하면 편하게 로깅을 구현할 수 있습니다.
주의할 점은 request의 body도 같이 로깅을 하고 싶을 수 있는데 현재 fastapi는 middleware에서 body를 활용할 수 없게 되어 있습니다.
코드로 좀 더 자세히 보도록 하겠습니다.
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
request_body = await request.body()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
@app.post("/hello_word")
async def hello_word(request: Request):
request_body = await request.body()
return {"message": "hello word"}
위와 같이 구현하면, middleware에서 await request.body()로 body를 가져오게 되면, hello_world 안에서 await request.body()를 호출할 때 block이 됩니다.
이 부분은 내부적으로, stream 형식으로 구현이 되어 있어서 한번 consume되면, 더 이상 가져올 수 없게 되어 이런 현상이 발생하고 있습니다.
그래서 조금 검색해보면, Request 객체 안에 state를 활용하여 body를 집어넣고 꺼내서 쓰는 방식이 많이 소개됩니다.
starlette에서는 위의 이슈에 대해서 아래의 PR에서 body를 reuse 할 수 있도록 구성을 변경하였고, fastapi에도 적용이 된다면 추후에는 middleware에서 request의 body까지 로그를 남길 수 있게 될 것 같습니다.
https://github.com/encode/starlette/pull/1692
'python > fastapi' 카테고리의 다른 글
[fastapi] lifespan (0) | 2024.03.03 |
---|---|
[fastapi] fastapi utils를 활용한 주기적인 작업(periodic task) 실행하기 (0) | 2023.06.18 |
[fastapi] background task (2) | 2023.06.04 |