一、概述
HTTPException異常拋出
再之前Bottle 中其實有一個就是HttpError異常類,在FastAPI也存在這么一個HTTPException。比如:
import uvicorn 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]} if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
在上面的代碼中,通過判斷item_id是不是存在於items來主動的拋出了一個404的錯誤
訪問一個錯誤的url
http://127.0.0.1:8000/items/asda

我們查看HTTPException和StarletteHTTPException的源碼發現他們也是繼承與Exception:
class HTTPException(StarletteHTTPException): def __init__( self, status_code: int, detail: Any = None, headers: dict = None ) -> None: super().__init__(status_code=status_code, detail=detail) self.headers = headers
所以我們對於異常通常可以直接的使用 raise來拋出異常。
HTTPException且返回新增自定義請求頭
import uvicorn 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]} if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
訪問一個錯誤的url
http://127.0.0.1:8000/items-header/asda

查看Headers,發現多了X-Error

自定義返回HTTPException
類似之前Bottle我們通過添加一個自定義的全局的錯誤,來統一的處理返回。FastAPI其實也提供一個自定義錯誤的機制:
官方示例如下:
import uvicorn 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} if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
觀察請求結果:
http://127.0.0.1:8000/unicorns/yolo

當請求name == yolo的時候,我們主動拋出了UnicornException,而且我們,@app.exception_handler(UnicornException)也捕獲到相關的異常信息,且返回了相關的信息。
覆蓋FastAPI默認的異常處理
按官方文檔說明就是,當請求包含無效的數據的時候,或參數提交異常錯誤的時候,會拋出RequestValidationError,
那其實我也可以通過上面的自定義異常的方式來覆蓋重寫我們的RequestValidationError所返回信息:
如: 默認代碼沒有添加覆蓋處理的話: 發生異常的時候是提示是:
import uvicorn from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import PlainTextResponse from starlette.exceptions import HTTPException as StarletteHTTPException from fastapi.responses import JSONResponse 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 JSONResponse({'mes':'觸發了RequestValidationError錯誤,,錯誤信息:%s 你妹的錯了!'%(str(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} if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
發生異常的請求下返回:
http://127.0.0.1:8000/items/yolo

恢復覆蓋的時候:
import uvicorn from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import PlainTextResponse from starlette.exceptions import HTTPException as StarletteHTTPException from fastapi.responses import JSONResponse 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 JSONResponse({'mes':'觸發了RequestValidationError錯誤,,錯誤信息:%s 你妹的錯了!'%(str(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} if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
請求結果:

上面的返回其實我們還可以修改一下返回如下,指定響應碼:
import uvicorn from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import PlainTextResponse from starlette.exceptions import HTTPException as StarletteHTTPException from fastapi.responses import JSONResponse from fastapi import FastAPI, Request,status from fastapi.encoders import jsonable_encoder 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: Request, exc: RequestValidationError): return JSONResponse( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}), ) @app.get("/items/{item_id}") async def read_item(item_id: int): if item_id == 3: # 注意fastapi包中的HTTPException才可以定義請求頭 raise HTTPException(status_code=418, detail="Nope! I don't like 3.") return {"item_id": item_id} if __name__ == '__main__': uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
再次請求一下

可以發現狀態碼是指定的422,返回信息也是指定的。
本文參考鏈接:
