FastAPI 進階知識(三) 錯誤處理


作者:麥克煎蛋   出處: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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM