異常處理
異常處理需要先從 fastapi 中引入 HTTPException,有異常的時候就 raise 一個 HTTPException 對象,該對象有一些屬性集乔,包括status_code颇玷、detail等笨农,例如:
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 對象的時候還可以加入自定義的頭,比如:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"X-Error": "There goes my error"},
)
自定義異常處理器
可以使用 @app.exception_handler() 裝飾器來自定義異常處理器帖渠,處理器函數(shù)需要有兩個參數(shù)谒亦,一個是 request 對象,一個是異常類的對象空郊,例如:
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}
覆寫默認的異常處理方法
我們可以從 fastapi.exceptions 中引入相應的異常類份招,然后再 @app.exception_handler裝飾器中把異常類作為參數(shù),就可以覆寫異常類的默認處理方法了狞甚,例如:
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}
此時再去訪問 http://localhost:8000/items/foo 就不會得到一個 JSON 的報錯信息锁摔,而是得到一個純文本的報錯消息。
使用 jsonable_encoder 來編碼數(shù)據(jù)
使用 fastapi.encoders 中的 jsonable_encoder 可以把數(shù)據(jù)編碼成 json 格式哼审,并自動做一些類型轉(zhuǎn)換鄙漏,比如把 python 中的日期型轉(zhuǎn)成字符串,例如:
from datetime import datetime
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
fake_db = {}
class Item(BaseModel):
title: str
timestamp: datetime
description: str = None
app = FastAPI()
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
fake_db[id] = json_compatible_item_data
print(json_compatible_item_data)
print(type(json_compatible_item_data))
return fake_db
整體更新與部分更新
我們在更新的時候棺蛛,如果參數(shù)的類定義中有默認值怔蚌,而傳入的參數(shù)中為指定明確的值,則生成的對象就會使用默認值旁赊,從而對數(shù)據(jù)進行整體更新桦踊,例如:
from typing import List, Optional
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = None
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
update_item_encoded = jsonable_encoder(item)
items[item_id] = update_item_encoded # 這種方式就是整體更新
return update_item_encoded
此時如果我們傳入這樣的 json 參數(shù):
{
"name": "Barz",
"price": 3,
"description": None,
}
在更新這三個字段的同時,還會把 tax 更新程默認的 10.5 终畅,這是不符合我們需求的籍胯。
部分更新,使用 .dict(exclude_unset=True)
使用 Pydantic 中的 .dict() 方法离福,降參數(shù) exclude_unset 設置為 True杖狼,就可以只更新我們給定的字段。通常在這種情況下妖爷,我們會使用 PATCH 請求而不是 POST 請求蝶涩,不過其實是都可以的。
from typing import List, Optional
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = None
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
@app.patch("/items/{item_id}", response_model=Item) # 這里也可以用 post
async def update_item(item_id: str, item: Item):
stored_item_data = items[item_id]
stored_item_model = Item(**stored_item_data)
update_data = item.dict(exclude_unset=True)
updated_item = stored_item_model.copy(update=update_data) # 拷貝一份出來更新
items[item_id] = jsonable_encoder(updated_item)
return updated_item