오늘 PR 리뷰를 하다가 현재 User에 대한 validate와 user 정보를 받아오는 함수를 정의해서 api args에서 annotate로 선언해서 썼다. 문제가 없는 코드였다.
API endpoint에 depends 주입한 코드
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def validate_token(token):
# 토큰이 유효하지 않다면
raise HTTPException(status_code=401, detail="Invalid Token")
# 토큰이 유효하다면
return token
def get_user_by_token(token):
# 1. token 이 유효한지 검사한다.
# 2. token 이 유효하다면 토큰에 심어져 있는 id 를 통해 db 에서 user 정보를 조회한다.
# 3. 만약 user 정보가 없다면 raise 를 일으킨다.
# 4. 해당 user의 active 가 true 인지 확인하고 아니라면 raise 를 일으킨다.
# 5. 모든 게 통과된다면 user 를 return 한다.
user = repository.get_user_by_token(token) # db 에서 user 를 가져온다.
if user is None:
raise HTTPException(status_code=404, detail="Invalid User")
if not user.active
raise HTTPException(status_code=404, detail="Invalid User")
return user
async def get_current_user(token: Annotated[str, Depends(validate_token)]):
user = get_user_by_token(token)
return user
@app.get("/users/me")
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
return current_user
위의 코드는 공식문서에 있는 코드를 변형해서 슈도 코드로 만들었다. read_users_me의 current_user 에 depends를 주입시켰다.
문제가 없는 코드이지만 나는 current_user는 인증이 요구되는 api endpoint 함수들에는 다 필요한 요소라고 생각했고, path router에다가 depends를 주입하면 코드 중복성을 제거할 수 있지 않을까 생각했다.
APIRouter에 depends를 주입해 보자
login_required_router = APIRouter(
dependencies=[Depends(get_current_user)],
)
@login_required_router("/api")
def get_foo(user: User):
do_something()
위와 같이 할 수 있지 않을까 생각했다. 결론적으로는 역할이 달랐고, return value를 endpoint에서 직접적으로 꺼내는 방법은 없었다.
- not used return value in path router depends
- include_router: How can I get the return of a dependency callable at the decorator level
하지만 내 생각에는 api endpoint 함수들마다 get_current_user를 annotate 인자로 받아서 쓰는 건 모든 api 마다 current user를 중복으로 쓰지 않을까란 생각이 들었다(결국 인증 - 유저가 유효한지- 에 관한 문제이기 때문에)결국엔 login_required 가 필요한 api endpoint에 공통적으로 들어가 있는 게 훨씬 낫다는 생각이었다. 따라서 request state에 담아서 쓰는 건 어떤가라는 생각이 들었다.
아래는 router에 depends를 주입하는 코드이다.
# router/user.py
async def get_current_user(request: Request, token: Annotated[str, Depends(validate_token)]):
user = get_user_by_token(token)
request.state.current_user = user
return user
login_required_router = APIRouter(
dependencies=[Depends(get_current_user)],
)
@login_required_router("/api")
def get_foo(request: Request):
current_user = request.state.current_user
do_something()
물론 이 코드도 문제는 request.state 에 current_user 가 저장되어 있다는 것을 알아야 하지만 내 생각에는 이게 중복성을 제거할 수 있지 않을까란 생각이 들었다.
ps. 글을 쓰고 다시 생각해보니 middleware에다가 현재 로그인한 사용자를 넣는건 어떨까란 생각이 드는데 올바른지 고민해봐야겠다
'FastAPI' 카테고리의 다른 글
FastAPI - Query Parameter에 Custom Validate 적용하기 (0) | 2023.09.02 |
---|---|
FastApi 사용하면서 기록할 것들 (0) | 2023.08.30 |
Annotated (0) | 2023.08.03 |
QueryParameter (0) | 2023.08.02 |
FastAPI - PathParameter (0) | 2023.08.02 |