1. 程式人生 > 實用技巧 >【FastAPI 學習 六】異常處理

【FastAPI 學習 六】異常處理

異常處理

統一捕獲處理異常,使得程式碼更加完善,健壯。
框架內建了一些異常,當然也可以自己定義異常然後捕獲處理。

完全可以參考官網(超喜歡這種風格的文件):
https://fastapi.tiangolo.com/tutorial/handling-errors/

異常捕獲

在FastAPI中,最常見的就是請求引數驗證異常處理,因為FastAPI全面使用了pydantic來做資料型別校驗,所以最常見的異常就是ValidationError , 然後FastAPI繼承了這個錯誤,專門捕獲請求引數異常的RequestValidationError

捕獲異常的語法

from fastapi.exceptions import RequestValidationError

@app.exception_handler(RequestValidationError)
async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
    """
    請求引數驗證異常
    :param request: 請求頭資訊
    :param exc: 異常物件
    :return:
    """
    # 日誌記錄異常詳細上下文 
    logger.error(f"全域性異\n{request.method}URL{request.url}\nHeaders:{request.headers}\n{traceback.format_exc()}")
    return response_code.resp_422(message=exc.errors())

自定義異常

我是這樣做的,在utils/custom_exc.py檔案下定義好各種異常

class UserTokenError(Exception):
    def __init__(self, err_desc: str = "使用者認證異常"):
        self.err_desc = err_desc


class UserNotFound(Exception):
    def __init__(self, err_desc: str = "沒有此使用者"):
        self.err_desc = err_desc

丟擲自定異常

先倒入異常,然後raise丟擲

from utils import custom_exc
# xxxx
raise custom_exc.UserTokenError(err_desc="access token fail")

捕獲自定異常

這個和內建異常捕獲語法是一樣的

@app.exception_handler(UserTokenError)
async def user_token_exception_handler(request: Request, exc: UserTokenError):
	"""
	使用者token異常
	:param request:
	:param exc:
	:return:
	"""
	logger.error(f"使用者認證異常\nURL:{request.url}\nHeaders:{request.headers}\n{traceback.format_exc()}")
	
	return response_code.resp_5000(message=exc.err_desc)

註冊異常

一般專案中會捕獲各種異常, 最好就是把異常函式集中起來,我是模仿Flask框架來寫的,所以FastAPI捕獲異常語法也類似

def create_app():
    """
    生成FatAPI物件
    :return:
    """
    app = FastAPI()

    # 其餘的一些全域性配置可以寫在這裡 多了可以考慮拆分到其他資料夾

    # 跨域設定
    # register_cors(app)

    # 註冊路由
    # register_router(app)

    # 註冊捕獲全域性異常
    register_exception(app)

    # 請求攔截
    # register_middleware(app)
    return app

def register_static_file(app: FastAPI) -> None:
	# 自定義異常 捕獲
    @app.exception_handler(UserNotFound)
    async def user_not_found_exception_handler(request: Request, exc: UserNotFound):
        """
        使用者認證未找到
        :param request:
        :param exc:
        :return:
        """
        logger.error(f"token未知使用者\nURL:{request.url}\nHeaders:{request.headers}\n{traceback.format_exc()}")

        return response_code.resp_5001(message=exc.err_desc)
        
    # 捕獲全部異常
    @app.exception_handler(Exception)
    async def all_exception_handler(request: Request, exc: Exception):
        """
        全域性所有異常
        :param request:
        :param exc:
        :return:
        """
        logger.error(f"全域性異常\n{request.method}URL:{request.url}\nHeaders:{request.headers}\n{traceback.format_exc()}")
        return response_code.resp_500(message="伺服器內部錯誤")

異常詳情

上一章部落格有提過,使用traceback列印詳細異常

import traceback
logger.error(traceback.format_exc())

總結

以上就是最基本的捕獲異常的寫法,捕獲好異常,可以幫助更快更友好的排錯,是介面返回格式更加規範。
注意 捕獲異常函式別手誤寫錯了,多加個s, 寫成@app.exception_handlers,就會報如下異常

@app.exception_handlers(UserTokenError)
TypeError: 'dict' object is not callable

對應程式碼Github地址

https://github.com/CoderCharm/fastapi-mysql-generator/blob/master/{{cookiecutter.project_name}}/app/common/custom_exc.py

見個人網站 https://www.charmcode.cn/article/2020-07-19_fastapi_exception