์ค๋ฅ ์ฒ๋ฆฌ¶
๋น์ ์ API๋ฅผ ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ์๊ฒ ์ค๋ฅ๋ฅผ ์๋ ค์ผํ๋ ๋ค์ํ ์ํฉ๋ค์ด ์์ต๋๋ค.
์ฌ๊ธฐ์ ํด๋ผ์ด์ธํธ๋ ํ๋ก ํธ์๋๊ฐ ์๋ ๋ธ๋ผ์ฐ์ , ๋ค๋ฅธ ์ฌ๋์ด ์์ฑํ ์ฝ๋, IoT ๊ธฐ๊ธฐ ๋ฑ์ด ๋ ์ ์์ต๋๋ค.
ํด๋ผ์ด์ธํธ์๊ฒ ๋ค์์ ์ฌ์ค์ ์ ๋ฌํ์ฌ์ผ ํฉ๋๋ค:
- ํด๋ผ์ด์ธํธ๊ฐ ํด๋น ์์ ์ ์ํํ๊ธฐ์ ์ถฉ๋ถํ ๊ถํ์ ๊ฐ์ง์ง ์์๋ค๋ ์ฌ์ค
- ํด๋ผ์ด์ธํธ๊ฐ ์์์ ์ ๊ทผํ ์ ์๋ค๋ ์ฌ์ค
- ํด๋ผ์ด์ธํธ๊ฐ ์ ๊ทผํ๋ ค๊ณ ํ๋ ํญ๋ชฉ์ด ์กด์ฌํ์ง ์๋๋ค๋ ์ฌ์ค
- ๊ธฐํ ๋ฑ๋ฑ
์ด๋ฌํ ๊ฒฝ์ฐ ์ผ๋ฐ์ ์ผ๋ก 4xx(400์์ 499๊น์ง)์ HTTP ์ํ ์ฝ๋๋ฅผ ๋ฐํํฉ๋๋ค.
์ด๊ฒ์ 2xx(200์์ 299๊น์ง)์ HTTP ์ํ ์ฝ๋์ ์ ์ฌํฉ๋๋ค. "2xx" ์ํ ์ฝ๋๋ค์ ์์ฒญ์ด "์ฑ๊ณต"์ ์ด์๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
400๋ฒ๋์ ์ํ ์ฝ๋๋ค์ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ์์ ์๋ฏธํฉ๋๋ค.
์ง๊ธ๊น์ง ๋ณธ "404 Not Found" ์ค๋ฅ๋ค์ ๋ ์ฌ๋ ค๋ณด์ธ์(๊ทธ๋ฆฌ๊ณ ๊ทธ์ ๋ํ ๋๋ด๋ค๋์)!
HTTPException ์ฌ์ฉ¶
ํด๋ผ์ด์ธํธ์๊ฒ HTTP ์๋ต์ ๋ฐํํ๊ธฐ ์ํด HTTPException์ ์ฌ์ฉํฉ๋๋ค.
HTTPException ์ํฌํธ¶
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": "The Foo Wrestlers"}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}
์ฝ๋์์ HTTPException ๋ฐ์์ํค๊ธฐ¶
HTTPException์ API์ ๋ํ ์ถ๊ฐ์ ์ธ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ ์ผ๋ฐ์ ์ธ ํ์ด์ฌ ์์ธ์
๋๋ค.
ํ์ด์ฌ ์์ธ์ด๊ธฐ ๋๋ฌธ์, ๋ฐํ(return)ํ์ง ์๊ณ ๋ฐ์(raise)์ํต๋๋ค.
์ด๊ฒ์ ๋ง์ฝ ๋น์ ์ด ๊ฒฝ๋ก ๋์ ํจ์์ ๋ด๋ถ์์ ํธ์ถํ๋ ์ ํธ๋ฆฌํฐ ํจ์ ๋ด๋ถ์ ์๊ณ , ํด๋น ์ ํธ๋ฆฌํฐ ํจ์ ๋ด๋ถ์์ HTTPException์ ๋ฐ์์ํค๋ ๊ฒฝ์ฐ, ๊ฒฝ๋ก ๋์ ํจ์์ ๋๋จธ์ง ๋ถ๋ถ์ ์คํํ๋ ๋์ ์ฆ์ ์์ฒญ์ ๋ํ ์์
์ ์ค๋จํ๊ณ HTTPException์ ๋ฐ๋ฅธ HTTP ์ค๋ฅ๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ์ ์กํ๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
์์ธ ์ฒ๋ฆฌ๋ฅผ ํจ์ ์์ด ๊ฐ์ ๋ฐํ(return)ํ๋ ๊ฒ๋ณด๋ค ๋ฐ์์ํค๋ ๊ฒ์ ์ด์ ์ ์ข
์ ๋ฐ ๋ณด์(Dependencies and Security) ์น์
์์ ๊น๊ฒ ๋ค๋ฃฐ ๊ฒ์
๋๋ค.
์ผ๋ก๋ก, ํด๋ผ์ด์ธํธ๊ฐ ์กด์ฌํ์ง ์๋ ํญ๋ชฉ์ ID๋ฅผ ์์ฒญํ๋ ๊ฒฝ์ฐ, ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์ํ ์ฝ๋ 404์ ํจ๊ป ์์ธ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์์ต๋๋ค.
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": "The Foo Wrestlers"}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}
๊ฒฐ๊ณผ ์๋ต¶
ํด๋ผ์ด์ธํธ๊ฐ http://example.com/items/foo(item_id๊ฐ "foo"์ธ ํญ๋ชฉ)๋ก ์์ฒญ์ ๋ณด๋๋ค๋ฉด, ํด๋ผ์ด์ธํธ๋ HTTP ์ํ์ฝ๋ 200๊ณผ ๋ค์๊ณผ ๊ฐ์ JSON ์๋ต์ ๋ฐ๊ฒ ๋ ๊ฒ์
๋๋ค:
{
"item": "The Foo Wrestlers"
}
ํ์ง๋ง ํด๋ผ์ด์ธํธ๊ฐ http://example.com/items/bar (item_id๊ฐ "bar"์ธ ํญ๋ชฉ ์์)๋ก ์์ฒญ์ ๋ณด๋ธ๋ค๋ฉด, ํด๋ผ์ด์ธํธ๋ HTTP ์ํ์ฝ๋ 404("์ฐพ์ ์ ์์(not found)" ์ค๋ฅ)์ ๋ค์์ JSON ์๋ต์ ๋ฐ๊ฒ ๋ ๊ฒ์
๋๋ค:
{
"detail": "Item not found"
}
ํ
HTTPException์ ๋ฐ์์ํฌ ๋, str ๋ฟ ์๋๋ผ JSON์ผ๋ก ๋ณํ ๊ฐ๋ฅํ ๋ชจ๋ ๊ฐ์ detail ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌํ ์ ์์ต๋๋ค.
dict, list ๋ฑ์ ์ ๋ฌํ ์ ์์ต๋๋ค.
์ด๋ค์ FastAPI์ ์ํด ์๋์ ์ผ๋ก ์ฒ๋ฆฌ๋๊ณ JSON์ผ๋ก ๋ณํ๋ฉ๋๋ค.
์ฌ์ฉ์ ์ ์ ํค๋ ์ถ๊ฐ¶
HTTP ์ค๋ฅ์ ์ฌ์ฉ์ ์ ์ ํค๋๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ์ ์ฉํ ๊ฒฝ์ฐ๋ค์ด ์์ต๋๋ค. ์๋ฅผ๋ค์ด, ๋ช๋ช ๋ณด์ ๋ฌธ์ ์ ๊ฒฝ์ฐ ๊ทธ๋ฌํฉ๋๋ค.
์ฝ๋์์ ์ด๊ฒ์ ์ง์ ์ฌ์ฉํ ํ์๋ ์์ ์ ์์ต๋๋ค.
๊ทธ๋ฌ๋ ๊ณ ๊ธ ์๋๋ฆฌ์ค์์ ํ์ํ ๊ฒฝ์ฐ, ์ฌ์ฉ์ ์ ์ ํค๋๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ๊ฐ๋ฅํฉ๋๋ค.
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": "The Foo Wrestlers"}
@app.get("/items-header/{item_id}")
async def read_item_header(item_id: str):
if item_id not in items:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"X-Error": "There goes my error"},
)
return {"item": items[item_id]}
์ฌ์ฉ์ ์ ์ ์์ธ ์ฒ๋ฆฌ๊ธฐ(exception handler) ์ค์น¶
Starlette๊ณผ ๋์ผํ ์์ธ ์ ํธ๋ฆฌํฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์ ์ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค.
๋น์ ๋๋ ๋น์ ์ด ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ฌ์ฉ์ ์ ์ ์์ธ์ธ UnicornException์ ๋ฐ์(raise) ์ํค๊ณ ์ ํ๋ค๊ณ ๊ฐ์ ํด๋ด
์๋ค.
๊ทธ๋ฆฌ๊ณ ๋น์ ์ FastAPI๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์์ธ๋ฅผ ์ ์ญ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์ ํฉ๋๋ค.
@app.exception_handler()๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์์ธ๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
class UnicornException(Exception):
def __init__(self, name: str):
self.name = name
app = FastAPI()
@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
return JSONResponse(
status_code=418,
content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
)
@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
if name == "yolo":
raise UnicornException(name=name)
return {"unicorn_name": name}
์ฌ๊ธฐ์ /unicorns/yolo๋ฅผ ์์ฒญํ๋ฉด, ๊ฒฝ๋ก ๋์์ UnicornException์ ๋ฐ์(raise)์ํฌ ๊ฒ์
๋๋ค.
ํ์ง๋ง ์ด๊ฒ์ unicorn_exception_handler ์ ์ํด ์ฒ๋ฆฌ๋ฉ๋๋ค.
๋ฐ๋ผ์ ๋น์ ์ HTTP ์ํ์ฝ๋๊ฐ 418์ด๊ณ , ๋ค์๊ณผ ๊ฐ์ JSON ๋ด์ฉ์ ๊ฐ์ง ์ค๋ฅ๋ฅผ ๋ฐ๊ฒ๋ฉ๋๋ค:
{"message": "Oops! yolo did something. There goes a rainbow..."}
๊ธฐ์ ์ธ๋ถ์ฌํญ
from starlette.requests import Request์ from starlette.responses import JSONResponse ์ญ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
FastAPI๋ ๊ฐ๋ฐ์์ธ ๋น์ ์ ํธ์๋ฅผ ์ํด fastapi.responses ์ ๋์ผํ starlette.responses ๋ ์ ๊ณตํฉ๋๋ค. ํ์ง๋ง ๋๋ถ๋ถ์ ์๋ต๋ค์ Starlette๋ก๋ถํฐ ์ง์ ์ ๊ณต๋ฉ๋๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก Request ๋ ๊ทธ๋ฌํฉ๋๋ค.
๊ธฐ๋ณธ ์์ธ ์ฒ๋ฆฌ๊ธฐ ์ฌ์ ์¶
FastAPI ์๋ ๊ธฐ๋ณธ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ค์ด ์์ต๋๋ค.
์ด ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ค์ ๋น์ ์ด HTTPException์ ๋ฐ์(raise)์ํค๊ฑฐ๋ ์์ฒญ์ ์ ํจํ์ง ์์ ๋ฐ์ดํฐ๊ฐ ์์ ๋ ๊ธฐ๋ณธ JSON ์๋ต๋ค์ ๋ฐํํ๋ ์ญํ ์ ํฉ๋๋ค.
์ด ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ค์ ์ง์ ์ฌ์ ์ ํ ์ ์์ต๋๋ค.
์์ฒญ ์ ํจ์ฑ ๊ฒ์ฌ ์์ธ ์ฌ์ ์¶
์์ฒญ์ ์ ํจํ์ง ์์ ๋ฐ์ดํฐ๊ฐ ์์ ๋, FastAPI๋ ๋ด๋ถ์ ์ผ๋ก RequestValidationError๋ฅผ ๋ฐ์์ํต๋๋ค.
๋ํ ์ด์ ๋ํ ๊ธฐ๋ณธ์ ์ธ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ ํฌํจํ๊ณ ์์ต๋๋ค.
์ด๋ฅผ ์ฌ์ ์ ํ๊ธฐ ์ํด, RequestValidationError๋ฅผ ์ํฌํธํ ํ@app.exception_handler(RequestValidationError) ๋ฐ์ฝ๋ ์ดํฐ์ ํจ๊ป ์์ธ ์ฒ๋ฆฌ๊ธฐ์ ์ฌ์ฉํ์ธ์.
์์ธ ์ฒ๋ฆฌ๊ธฐ๋ Request์ ์์ธ๋ฅผ ์ ๋ฌ๋ฐ์ต๋๋ค.
from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
app = FastAPI()
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return PlainTextResponse(str(exc), status_code=400)
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
return {"item_id": item_id}
์ด์ /items/foo๋ก ์์ฒญ์ ๋ณด๋ด๋ฉด, ๊ธฐ๋ณธ JSON ์ค๋ฅ๋ฅผ ๋ฐ๋ ๋์ :
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
๋ค์๊ณผ ๊ฐ์ ํ ์คํธ๋ฅผ ๋ฐ๊ฒ ๋ ๊ฒ์ ๋๋ค:
1 validation error
path -> item_id
value is not a valid integer (type=type_error.integer)
RequestValidationError vs ValidationError¶
์ฃผ์
์ด ๋ถ๋ถ์ ๋น์ ์๊ฒ ์ง๊ธ ์ค์ํ์ง ์๋ค๋ฉด ๋์ด๊ฐ๋ ๋ฌด๊ดํ ๊ธฐ์ ์ธ๋ถ์ฌํญ์ ๋๋ค.
RequestValidationError ๋ Pydantic์ ValidationError์ ํ์ ํด๋์ค์
๋๋ค.
FastAPI๊ฐ ์ด๊ฒ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋น์ ์ด response_model์์ Pydantic ๋ชจ๋ธ์ ์ฌ์ฉํ๊ณ , ๋น์ ์ ๋ฐ์ดํฐ์ ์ค๋ฅ๊ฐ ์๋ ๊ฒฝ์ฐ ๋ก๊ทธ์์ ์ค๋ฅ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
ํ์ง๋ง ํด๋ผ์ด์ธํธ์ ์ฌ์ฉ์๋ ์ด๋ฅผ ๋ณผ ์ ์์ต๋๋ค. ๋์ , ํด๋ผ์ด์ธํธ๋ HTTP ์ํ ์ฝ๋ 500 ๊ณผ ํจ๊ป "๋ด๋ถ ์๋ฒ ์ค๋ฅ(Internal Server Error)"๋ฅผ ์ ๋ฌ ๋ฐ์ต๋๋ค.
๋ง์ฝ ํด๋ผ์ด์ธํธ์ ์์ฒญ(request)์ด ์๋ ์๋ต(response)์ด๋ ์ฝ๋ ์ด๋๊ฐ์ Pydantic์ ValidationError ๊ฐ ์๋ ๊ฒฝ์ฐ, ์ด๊ฒ์ ์ฝ๋ ๋ด์ ๋ฒ๊ทธ๊ฐ ์์์ ์๋ฏธํ๊ธฐ ๋๋ฌธ์
๋๋ค.
๋น์ ์ด ํด๋น ๋ฒ๊ทธ๋ฅผ ํด๊ฒฐํ๋ ๋์ ๋ณด์ ์ทจ์ฝ์ ์ด ๋ ธ์ถ๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด ํด๋ผ์ด์ธํธ์ ์ฌ์ฉ์๋ ์ค๋ฅ์ ๊ดํ ๋ด๋ถ ์ ๋ณด์ ์ ๊ทผํ ์ ์์ต๋๋ค.
HTTPException ์์ธ ์ฒ๋ฆฌ๊ธฐ ์ฌ์ ์¶
๊ฐ์ ๋ฐฉ์์ผ๋ก HTTPException ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ฌ์ ์ํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์ด ์ค๋ฅ๋ค์ ๋ํด JSON ๋์ ์ผ๋ฐ ํ ์คํธ๋ฅผ ๋ฐํํ๊ณ ์ ํ ์ ์์ต๋๋ค:
from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
app = FastAPI()
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return PlainTextResponse(str(exc), status_code=400)
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
return {"item_id": item_id}
๊ธฐ์ ์ธ๋ถ์ฌํญ
from starlette.responses import JSONResponse ์ญ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
FastAPI๋ ๊ฐ๋ฐ์์ธ ๋น์ ์ ํธ์๋ฅผ ์ํด fastapi.responses์ ๋์ผํ starlette.responses๋ ์ ๊ณตํฉ๋๋ค. ํ์ง๋ง ๋๋ถ๋ถ์ ์๋ต๋ค์ Starlette๋ก๋ถํฐ ์ง์ ์ ๊ณต๋ฉ๋๋ค.
RequestValidationError ๋ณธ๋ฌธ ์ฌ์ฉ¶
RequestValidationError ๋ ์ ํจํ์ง ์์ ๋ฐ์ดํฐ์ ํจ๊ป ๋ฐ์ body๋ฅผ ํฌํจํฉ๋๋ค.
๋น์ ์ ์ดํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ๋ ๋์ ๋ณธ๋ฌธ์ ๋ก๊ทธ์ ๊ธฐ๋กํ๊ณ , ๋๋ฒ๊น ํ๊ณ , ์ฌ์ฉ์์๊ฒ ๋ฐํํ๋ ์์ ๋ฑ์ ์ํํ๋ ๋ฐ์ ์ด๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
from fastapi import FastAPI, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
)
class Item(BaseModel):
title: str
size: int
@app.post("/items/")
async def create_item(item: Item):
return item
๋ค์๊ณผ ๊ฐ์ด ์ ํจํ์ง ์์ ํญ๋ชฉ์ ์ ์กํ๋ฉด:
{
"title": "towel",
"size": "XL"
}
๋ฐ์ดํฐ๊ฐ ์ ํจํ์ง ์์์ ์๋ ค์ฃผ๋, ์ ๋ฌ๋ฐ์ ๋ณธ๋ฌธ์ ํฌํจํ ์๋ต์ ๋ฐ๊ฒ ๋ ๊ฒ์ ๋๋ค.
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
FastAPI์ HTTPException vs Starlette์ HTTPException¶
FastAPI์๋ ์์ฒด์ ์ธ HTTPException ์ด ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ FastAPI์ HTTPException ์ค๋ฅ ํด๋์ค๋ Starlette์ HTTPException ์ค๋ฅ ํด๋์ค๋ฅผ ์์๋ฐ์ต๋๋ค.
์ ์ผํ ์ฐจ์ด์ ์, FastAPI์ HTTPException ์ ์ฌ์ฉํ ๊ฒฝ์ฐ ์๋ต์ ํค๋๋ฅผ ์ถ๊ฐํ ์ ์๋ค๋ ๊ฒ์
๋๋ค.
์ด๊ฒ์ OAuth 2.0 ๋ฐ ๋ช๋ช ๋ณด์ ์ ํธ๋ฆฌํฐ์ ๋ด๋ถ์ ์ผ๋ก ํ์/์ฌ์ฉ๋ฉ๋๋ค.
๋ฐ๋ผ์, ํ์์ ๊ฐ์ด ์ฝ๋์์ FastAPI์ HTTPException ์ ๊ณ์ ๋ฐ์์ํฌ ์ ์์ต๋๋ค.
๊ทธ๋ฌ๋ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ๋ฑ๋กํ ๋์๋, Starlette์ HTTPException ์ ๋ํ์ฌ ๋ฑ๋กํด์ผ ํฉ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด Starlette์ ๋ด๋ถ ์ฝ๋ ๋๋ Starlette ํ์ฅ(extension) ๋๋ ํ๋ฌ๊ทธ์ธ์ ์ผ๋ถ๊ฐ Starlette HTTPException์ ๋ฐ์์ํฌ ๋, ํด๋น ์์ธ ์ฒ๋ฆฌ๊ธฐ๊ฐ ์ด๋ฅผ ํฌ์ฐฉํ๊ณ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
์๋ ์์์์, ๋ ๊ฐ์ HTTPException ์ ๊ฐ์ ์ฝ๋์์ ์ฌ์ฉํ๊ธฐ ์ํด, Starlette์ ์์ธ๋ StarletteHTTPException ๋ก ์ด๋ฆ์ ๋ณ๊ฒฝํ์์ต๋๋ค.
from starlette.exceptions import HTTPException as StarletteHTTPException
FastAPI์ ์์ธ ์ฒ๋ฆฌ๊ธฐ ์ฌ์ฌ์ฉ¶
์ด๋ ํ ๋ฐฉ์์ผ๋ก๋ ์์ธ๋ฅผ ์ฌ์ฉํ๋ฉด์, FastAPI์ ๋์ผํ ๊ธฐ๋ณธ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
fastapi.exception_handlers ๋ก๋ถํฐ ๊ธฐ๋ณธ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ํฌํธํ๊ณ ์ฌ์ฌ์ฉํฉ๋๋ค:
from fastapi import FastAPI, HTTPException
from fastapi.exception_handlers import (
http_exception_handler,
request_validation_exception_handler,
)
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
app = FastAPI()
@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):
print(f"OMG! An HTTP error!: {repr(exc)}")
return await http_exception_handler(request, exc)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
print(f"OMG! The client sent invalid data!: {exc}")
return await request_validation_exception_handler(request, exc)
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
return {"item_id": item_id}
์๊ธฐ ์์์์, ๊ฐ์ ์ด ๋งค์ฐ ๋ง์ด ์์ธ ๋ฉ์์ง์ ํจ๊ป ์ค๋ฅ๋ฅผ ๋จ์ํ print ํ์ต๋๋ค.
ํ์ง๋ง ์ด๋ฅผ ํตํด ์์ธ๋ฅผ ์ฌ์ฉํ ํ, ๊ธฐ๋ณธ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ๋ค์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์์ ๊ฒ์ ๋๋ค.