본문 바로가기
기타

[Python/FastAPI] 1. API 정의 - HTTP Request (2)

2023. 3. 17.
반응형

지난 포스트에서는 FastAPI에서 HTTP API를 정의할 때 Path Operation과 Path parameter를 정의하는 방법에 대해 알아보았습니다. 이번 포스트에서는 Query parameter와 Request body를 정의하는 방법을 알아보겠습니다.

 

[Python/FastAPI] 0. Overview

[Python/FastAPI] 1. API 정의 - HTTP Request (1)

 

 

FastAPI에서의 Query Parameter 정의

# query parameter 이용
@app.get("/items")
def read_item(page: int = 0, size: int = 10):
	return items[page * size: (page + 1) * size]

# optional 설정
@app.get("/items/{item_id}")
def read_item(item_id: int, query: Union[str, None] = None):
	if query:
		return {"item_id": item_id, "query": query}
	return {"item_id": item_id}
  • 쿼리 파라미터는 URL에 포함되므로 항상 문자열이지만, 타입을 지정해주면 FastAPI가 검증 및 파싱해준다.
  • 쿼리 파라미터는 경로에서 필수적으로 고정되는 부분이 아니기 때문에 선택적이므로 기본값을 지정해줄 수 있다.
    • 기본값을 설정하면 파라미터 값이 없을 때 해당 값이 할당된다.
    • 기본값을 None으로 설정하면 optional 파라미터로 선언할 수 있다.
  • bool 타입으로 지정하면 아래의 값들을 bool 타입으로 변환해준다.
    • 1, True, true, on, yes 등 → True
    • 0, False, false, off, no 등 → False
  • List 타입으로 지정하면 해당 쿼리 파라미터의 리스트 생성해준다.
    • q: Union[List[str], None] = Query(default=["a", "b", "c"])
    • …?q=foo&q=bar
    • → q: [”foo”, “bar”]
  • List 타입으로 지정하면서 기본값 Query를 지정해주지 않으면 Request body로 인식한다.

Query로 추가기능 사용하기

from fastapi import Query

...

# use Query as default
@app.get("/items")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):
	results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
	if q:
		results.update({"q", q})
	return results


# q is required
@app.get("/items")
async def read_items(q: Union[str, None] = Query(max_length=50)):
	results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
	if q:
		results.update({"q", q})
	return results
  • fastapi의 Query를 사용해서 쿼리 파라미터에 제약조건을 추가할 수 있다.
    • 쿼리 파라미터는 항상 string이므로 max_length, min_length, regex를 설정할 수 있다.
    • 이 방식은 Query() 자체를 해당 파라미터의 기본값으로 사용하게 되는데, 해당 쿼리 파라미터를 required하게 만드려면 Query() 내부에 default 옵션을 정의하지 않으면 된다.
    • required 파라미터임을 명시적으로 표시하고 싶다면 아래의 방법들을 사용하면 된다.
      • q: Union[str, None] = Query(default=..., max_length=50)
      • q: Union[str, None] = Query(default=Required, max_length=50)
      • (from pydantic import Required)
  • 해당 파라미터의 입력명과, 파라미터 이름을 다르게 하려면 아래처럼 alias를 설정해주면 된다.
    • q: Union[str, None] = Query(default=None, alias="item-query")
    • → 요청에서는 item-query를 사용하고, 코드에서는 q를 사용한다.
  • 파라미터 deprecated 시키기
    • 더이상 사용하지 않는 쿼리 파라미터지만 클라이언트가 계속해서 해당 값을 요청에 포함하는경우, Query(…, deprecated=True) 값만 추가하면 deprecated 되었음을 문서에 표현해준다.

 

FastAPI에서 Request body 정의

body에 순수 list 또는 임의의 dict 받기

@app.post("/tags")
async def create_multiple_tags(tags: List[str]):
	return tags

@app.post("/kv-store")
async def get_kv_store(kv: Dict[int, str]):
	return kv
  • List 또는 Dict 자체를 파라미터로 넣어 요청 바디로 받을 수 있다.

 

BaseModel로 Request body에 객체 받기

from pydantic import BaseModel

class Item(BaseModel):
	name: str
	description: Union[str, None] = None
	price: float
	tax: Union[str, None] = None
	tags: List[str] = []

...

@app.post("/items")
async def create_item(item: Item):
	return item
  • pydantic의 BaseModel을 상속하는 서브 클래스를 생성한다.
    • 파라미터의 경우와 마찬가지로 기본값을 설정할 수 있으며, 기본값이 None으로 설정되면 optional 조건을 줄 수 있다.
  • 파라미터에 해당 서브 클래스 타입을 지정하면 FastAPI가 아래의 동작들을 수행한다.
    1. HTTP 요청 바디를 JSON으로 읽어온다.
    2. 데이터를 검증한다.
      • 데이터가 invalid하면 어디가 문제인지에 대한 정보를 포함하여 에러를 반환한다.
    3. 정의한 파라미터로 데이터를 전달해준다. (item)

 

여러개의 BaseModel 파라미터

class Item(BaseModel):
	name: str
	description: Union[str, None] = None
	price: float
	tax: Union[str, None] = None

class User(BaseModel):
	username: str
	full_name: Union[str, None] = None

...

@app.post("/items")
async def create_item(item: Item, user: User):
	return {"item": item, "user": user}
  • 한 메소드에 대해 여러개의 파라미터가 BaseModel을 상속하는 타입이라면, FastAPI는 파라미터의 이름을 key로 사용하여 요청 바디를 파싱한다.
{
    "item":
        { "name": "Foo", "description": "The pretender", "price": 42.0, "tax": 3.2 },
    "user":
        { "username": "dave", "full_name": "Dave Grohl" }
}

"item" 부분을 item 파라미터에, "user" 부분을 user 파라미터에 각각 매핑해준다.

→ 한 개만 사용하는 경우에는 "item"과 같은 별도의 Key가 필요 없다.

 

Body로 Request body 받기

  • singlular 타입의 값을 다른 객체 타입들과 함께 요청 바디로 받기 위해서는 fastapi의 Body를 사용하면 된다.
import fastapi import Body

...

@app.post("/items")
async def create_item(item: Item, user: User, count: int = Body()):
	return {"item": item, "user": user, "count": count}
{
    "item": { "name": "Foo", "description": "The pretender", "price": 42.0, "tax": 3.2 },
    "user": { "username": "dave", "full_name": "Dave Grohl" },
    "count": 5
}
  • Path의 경우처럼 제약조건 추가도 가능하다.
    • gt, ge, lt, le
  • embed 설정을 통해서 요청 바디에 대한 파라미터가 하나만 존재하는 경우에도 key와 매핑되게 할 수 있다.
@app.post("/items")
async def create_item(item: Item = Body(embed=True)):
	return item
{
    "item": {
                    "name": "Foo", "description": "The pretender", "price": 42.0, "tax": 3.2
                  }
}

 

Field로 클래스 멤버 변수 검증하기

from pydantic import BaseModel, Field

...

class Item(BaseModel):
	name: str
	description: Union[str, None] = Field(default=None, title="The description of the item", max_length=300)
	price: float = Field(gt=0, description="The price must be greater than zero")
	tax: Union[str, None] = None

...

@app.post("/items")
async def create_item(item: Item):
	return item
  • pydantic의 Field도 fastapi의 Path, Query, Body처럼 동작한다.

 

중첩 모델 사용하기

from pydantic import BaseModel

class Image(BaseModel):
	url: str
	name: str

class Item(BaseModel):
	name: str
	description: Union[str, None] = None
	price: float
	tax: Union[str, None] = None
	tags: List[str] = []
	image: Union[Image, None] = None

...

@app.post("/items")
async def create_item(item: Item):
	return item
{
    "name": "Foo",
    "description": "The pretender",
    "price": 42.0,
    "tax": 3.2,
    "tags": ["rock", "metal", "bar"],
    "image": { "url": "http://example.com/baz.jpg", "name": "The Foo live" }
}
  • 클래스의 멤버 변수로 다른 클래스 타입을 갖는 변수를 선언하여 사용할 수 있다.

 

FastAPI에서의 Header와 Cookie

  • fastapi의 Header를 이용해서 헤더값을 파라미터로 받을 수 있다.
from fastapi import Header

...

@app.get("/items/")
async def read_items(user_agent: Union[str, None] = Header(default=None)):
    return {"User-Agent": user_agent}
  • HTTP header에는 보통 - 문자를 사용하는데, 파이썬에서는 이를 변수 이름에 사용하지 않는다.
    • 만약 이 설정을 사용하고 싶지 않다면 Header 옵션 중 convert_underscores를 False로 설정한다.
  • → FastAPI는 파라미터의 _ 문자를 자동적으로 -로 변환하여 헤더값과 매핑한다.
@app.get("/items/")
async def read_items(x_token: Union[List[str], None] = Header(default=None)):
    return {"X-Token values": x_token}
  • 동일한 이름의 헤더가 여러개가 있는 경우를 위해 List 타입으로도 받을 수 있다.
from fastapi import Cookie

...

@app.get("/items/")
async def read_items(ads_id: Union[str, None] = Cookie(default=None)):
	return {"ads_id": ads_id}
  • fastapi의 Cookie를 이용해서 쿠키값을 파라미터로 받을 수 있다.
728x90
반응형

댓글