์ค๋ฅ ์ฒ๋ฆฌ¶
๋น์ ์ 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
ํ์ต๋๋ค.
ํ์ง๋ง ์ด๋ฅผ ํตํด ์์ธ๋ฅผ ์ฌ์ฉํ ํ, ๊ธฐ๋ณธ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ๋ค์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์์ ๊ฒ์ ๋๋ค.