作者:麥克煎蛋 出處:https://www.cnblogs.com/mazhiyong/ 轉載請保留這段聲明,謝謝!
如果使用API時有錯誤發生,你需要通知給客戶端(Web端或者API使用者)這個錯誤信息。
常見的錯誤信息為:
- 客戶端沒有權限進行相關的操作。
- 客戶端找不到對應的路徑操作。
- 客戶端找不到對應的資源。
- 其他。
這些錯誤信息的HTTP狀態碼一般為400錯誤(400~499)。
一、HTTPException
我們用HTTPException
模塊返回帶錯誤信息的Response。
HTTPException
是一個普通的Python異常,同時帶有與API訪問有關的附加數據。
1、導入模塊
from fastapi import HTTPException
2、拋出異常
在代碼中拋出HTTPException
。
raise HTTPException(status_code=404, detail="Item not found")
這里, 參數detail除了可以傳遞字符串,還可以傳遞任何可以轉換成JSON格式的數據,如dict、list等。
代碼示例:
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錯誤,在一些場景下,我們需要添加自定義頭信息。
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]}
三、自定義異常處理器
借助 the same exception utilities from Starlette,我們可以添加自定義異常處理器。
假設我們有個自定義異常 UnicornException
,我們想在全局范圍內處理這個異常。
借助 @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,這個異常會被我們的異常處理器unicorn_exception_handler捕獲到。
最后我們收到的HTTP錯誤碼就是418,並且錯誤內容為:
{"message": "Oops! yolo did something. There goes a rainbow..."}
四、重寫缺省異常處理器
FastAPI有一些缺省的異常處理器。當我們拋出HTTPException
異常或者當請求有非法數據的時候,這些處理器負責返回默認的JSON結果。
我們可以重寫這些異常處理器。
1、重寫請求校驗異常處理器
當一個請求包含非法數據的時候,FastAPI內部會拋出RequestValidationError
異常,並且有默認的異常處理器來處理。
我們可以用 @app.exception_handler(RequestValidationError)
來重寫這個異常處理器。
from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import PlainTextResponse app = FastAPI() @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
,那么返回結果就不是默認的:
{ "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
有個
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", "item", "size" ], "msg": "value is not a valid integer", "type": "type_error.integer" } ], "body": { "title": "towel", "size": "XL" } }
2、重寫HTTPException
異常處理器
同樣的方法,我們可以重寫HTTPException
異常處理器。
例如,你可能想返回純文本格式而不是JSON格式的錯誤信息。
from fastapi import FastAPI, HTTPException 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}
如果請求 /items/3,這時候返回的錯誤信息為:
Nope! I don't like 3.
如果沒有自定義異常處理器http_exception_handler,返回的錯誤信息為:
{ "detail": "Nope! I don't like 3." }
五、重用缺省異常處理器
我們可以導入並且重用缺省的異常處理器。
我們從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!: {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}
在示例中,我們在拋出異常之前添加了一條日志輸出。我們可以根據業務需求靈活的重用缺省異常處理器。
六、FastAPI HTTPException
對比 Starlette HTTPException
HTTPException
對比 Starlette HTTPException
FastAPI HTTPException
繼承自 Starlette's HTTPException
。
唯一的區別是,FastAPI HTTPException
允許你在response添加頭信息。主要在內部用於OAuth 2.0以及一些安全相關的功能。
因此,通常我們在代碼中拋出FastAPI HTTPException
異常。
但是,當我們注冊異常處理器的時候,我們應該注冊為Starlette HTTPException
。
這樣,當Starlette的內部代碼或者Starlette擴展插件拋出Starlette HTTPException
時,我們的處理器才能正常捕獲和處理這個異常。
如果我們要在代碼中同時使用這兩個類,為了避免命名沖突,我們可以重命名其中一個類。
from fastapi import HTTPException from starlette.exceptions import HTTPException as StarletteHTTPException