全面擁抱FastApi —優(yōu)雅的返回異常錯誤
第一時間獲取 Python 技術(shù)干貨!

在開發(fā)接口或者服務(wù)的時候,經(jīng)常會遇到需要給客戶端返回異常錯誤
例如:
用戶操作權(quán)限不夠 參數(shù)錯誤 請求的資源不存在..
眾所周知,因客戶端或調(diào)用方的原因?qū)е鲁鲥e的,返回的狀態(tài)碼是以 4 開頭的 (400~499)
比如常見的 404 Not Found, 資源不存在...
為了直觀友好的給客戶端返回錯誤, 在 FastApi 中一般使用 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]}
當遇到用戶請求異常的時候,可以選擇用 raise 將異常拋出去
拋出異常,便立即會結(jié)束本次請求,并將HTTP錯誤從HTTPException發(fā)送到客戶端或瀏覽器
比如:在瀏覽器中輸入 http://127.0.0.1:8000/items/jerry
由于 jerry 并不在 items 中,瀏覽器便會收到 404 以及一個 json 格式的 response

注意:這個 json 由 FastAPI 自動處理并轉(zhuǎn)換的。
自定義異常類
和 starlette 源碼中處理異常一樣,你也可以自定義一個異常處理類
定義的異常處理類,使用 @app.exception_handler() 支持在 FastAPI 中全局使用該異常類
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..."},
????)
在路由函數(shù)中,使用該類
@app.get("/unicorns/{name}")
async?def?read_unicorn(name:?str):
????if?name?==?"yolo":
????????raise?UnicornException(name=name)
????return?{"unicorn_name":?name}
運行服務(wù)后,請求服務(wù)路徑 /unicorns/yolo
在客戶端就能收到一個提示友好,并事先定義好狀態(tài)碼 418 的提示錯誤

重寫默認異常類
FastAPI 有許多的默認異常處理類
這些處理程序負責在引發(fā) HTTPException 和請求包含無效數(shù)據(jù)時返回默認 JSON 響應(yīng)
比如下面的路由是只支持 item_id 為 int 類型的路徑函數(shù)
@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}
當從客戶端傳過來的 item_id 為非 int 類型的時候,便返回默認的 JSON 響應(yīng)

你可以重寫這些默認的異常處理類,變成自定義的。比如
重寫請求驗證異常類
當一個請求包含非法的請求數(shù)據(jù)時,會觸發(fā) FastAPI 中的 RequestValidationError
為了重寫該異常處理類,需要導(dǎo)入 RequestValidationError, 并使用 @app.exception_handler(RequestValidationError) 對異常處理函數(shù)進行裝飾
from?fastapi.exceptions?import?RequestValidationError
@app.exception_handler(RequestValidationError)
async?def?validation_exception_handler(request,?exc):
????return?PlainTextResponse(str(exc),?status_code=400)
將這部分代碼,和上面的代碼合并后運行。再次請求會看到不一樣的提示~~~

重寫 HTTPException
同樣,你也可以重寫 HTTPException 處理程序
比如你想返回文本的錯誤提示,而不是默認的 JSON 格式錯誤提示
和上面一樣,使用 @app.exception_handler(HTTPException) 裝飾異常處理函數(shù)即可
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.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}
返回異常請求body
當接收到非法請求的時候,RequestValidationError 中包含異常請求體的,只是沒有給我們返回
但是在開發(fā)應(yīng)用程序或者與前端聯(lián)調(diào)的時候,可以將請求體加到返回的 response 中
這樣在出現(xiàn)問題的時候,可以通過日志或響應(yīng),快速定位到問題!
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
我們來觸發(fā)下異常,比如請求體:
{
??"title":?"towel",
??"size":?"XL"
}
對于異常請求,收到的響應(yīng)中就會包含該次異常請求的請求 body
{
??"detail":?[
????{
??????"loc":?[
????????"body",
????????"size"
??????],
??????"msg":?"value?is?not?a?valid?integer",
??????"type":?"type_error.integer"
????}
??],
??"body":?{
????"title":?"towel",
????"size":?"XL"
??}
}
在聯(lián)調(diào)或開發(fā)的時候,可以節(jié)省一些不必要的時間,提高效率!
推薦閱讀

全面擁抱FastApi —三大參數(shù)及驗證

全面擁抱 FastApi — 多應(yīng)用程序項目結(jié)構(gòu)規(guī)劃
