python/fastapi

[fastapi] Middleware

seokhyun2 2023. 7. 2. 17:22

오늘은 fastapi의 middleware에 대해서 알아보려고 합니다.

https://fastapi.tiangolo.com/tutorial/middleware/

 

Middleware - FastAPI

FastAPI framework, high performance, easy to learn, fast to code, ready for production

fastapi.tiangolo.com

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

 

Reuse Request's body buffer for call_next in BaseHTTPMiddleware by adriangb · Pull Request #1692 · encode/starlette

Thought of this while working on related work to #1691. Sorry if this has already been proposed, I know we've gone around a couple times with similar stuff. Background: Closes #848 Closes #1519 Cl...

github.com