四、Python框架篇: FastApi-错误处理

1.验证错误

默认返回

当传参不符合模型验证规则时,默认错误信息和格式返回如下:

{
  "detail": [
    {
      "type": "list_type",
      "loc": [
        "body",
        "likes"
      ],
      "msg": "Input should be a valid list",
      "input": "足球"
    }
  ]
}

在上篇文章FastApi-响应模型中,我们强调的是接口对外输出结构应该一致,所以即便参数错误应该输出以下结构:

{
    "code":-1,
    "msg":"具体错误信息...",
    "data": null
}

按照官方文档说法,我们需要自定义错误处理器,并以此来覆盖框架默认的异常处理器,参数验证错误处理器默认走的RequestValidationError,所以覆盖它就行,下面是实现步骤

(1)自定义处理器

新建包 errors,并新增文件 validation_error.py,文件内容如下:

from fastapi import Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from parameter import http_resp
from fastapi.encoders import jsonable_encoder

async def validationExceptionHandler(request: Request, exc: RequestValidationError):
    """ 自定义参数验证异常错误"""
    print("自定义参数验证异常错误")
    errMsg = ""
    for error in exc.errors():
        errMsg += ".".join(error.get("loc")) + ":" + error.get("msg") + ";"


    # 这里response.ResponseFail是上篇文章中的内容
    return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder(http_resp.ResponseFail(errMsg)))

在包errors/__init__.py引用,并封装统一注册方法:

from fastapi import FastAPI
from validation_error import validationExceptionHandler
from fastapi.exceptions import RequestValidationError

def registerCustomErrorHandle(server: FastAPI):
    """ 统一注册自定义错误处理器"""
    # 注册参数验证错误,并覆盖模式RequestValidationError
    server.add_exception_handler(RequestValidationError, validationExceptionHandler)

(2)注册与覆盖

   在main.py中调用registerCustomErrorHandle

import uvicorn
from fastapi import Depends,FastAPI


from router import RegisterRouterList
import errors


app = FastAPI(redoc_url=None, docs_url="/apidoc", title="FastAPI学习")
# 加载路由
for item in RegisterRouterList:
    app.include_router(item.router)


# 注册自定义错误处理器
errors.registerCustomErrorHandle(app)


@app.get("/")
async def index():
    """
    注册一个根路径
    :return:
    """
    return {"message": "Hello World"}


@app.get("/info")
async def info():
    """
    项目信息
    :return:
    """
    return {
        "app_name": "FastAPI框架学习",
        "app_version": "v0.0.1"
    }


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

2.路由错误

路由错误常见的一般分为以下两种:

  • 404:访问不存在的接口地址;

  • 405:接口定义的请求方式是POST, 当时使用GET方式请求时;

2.1 默认返回

// 当访问不存在路由时
{
    "detail": "Not Found"
}
// 当访问方式不对时
{
    "detail": "Method Not Allowed"
}

2.2 自定义处理器

新建包 errors,并新增文件http_error.py,文件内容如下:

from fastapi import status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException
from parameter.http_resp import HttpResponse


async def httpExceptionHandler(request, exc: HTTPException) -> JSONResponse:
    """自定义处理HTTPException"""
    print("request:", request)
    print("status_code:", exc.status_code)
    if exc.status_code == status.HTTP_404_NOT_FOUND:
        # 处理404错误
        return JSONResponse(
            content=jsonable_encoder(HttpResponse.ResponseFail("接口路由不存在~")),
            status_code=status.HTTP_200_OK,
        )
    elif exc.status_code == status.HTTP_405_METHOD_NOT_ALLOWED:
        # 处理405错误
        return JSONResponse(
            content=jsonable_encoder(HttpResponse.ResponseFail("请求方式错误,请查看文档确认~")),
            status_code=status.HTTP_200_OK,
        )
    else:
        return JSONResponse(
            content=jsonable_encoder(HttpResponse.ResponseFail(str(exc))),
            status_code=status.HTTP_200_OK,
        )

2.3 注册&覆盖

修改 errors/__init__.py文件中的统一注册方法registerCustomErrorHandle:

from fastapi import FastAPI
from errors.validation_error import validationExceptionHandler
from http_error import httpExceptionHandler
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException

def registerCustomErrorHandle(server: FastAPI):
    """ 统一注册自定义错误处理器"""
    # 注册参数验证错误,并覆盖模式RequestValidationError
    server.add_exception_handler(RequestValidationError, validationExceptionHandler)
    # 错误处理StarletteHTTPException
    server.add_exception_handler(StarletteHTTPException, httpExceptionHandler)

注意:这里覆盖的错误是:starlette.exceptions包中的HTTPException,不是这个包fastapi.exceptions,否则不会生效!

2.4 验证

//当访问不存在路由时

{
  "code": 200,
  "msg": "处理成功",
  "data": "接口路由不存在~"
}

3.系统错误

在厉害的程序员都无法避免程序错误,特别是运行中遇到的异常。如果遇到异常时,我们不希望用户看到500,或者页面崩溃。

3.1 默认返回

Traceback (most recent call last):
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 419, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\fastapi\applications.py", line 1054, in __call__
    await super().__call__(scope, receive, send)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\applications.py", line 123, in __call__
    await self.middleware_stack(scope, receive, send)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\middleware\errors.py", line 186, in __call__
    raise exc
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\middleware\errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\middleware\exceptions.py", line 65, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\routing.py", line 754, in __call__
    await self.middleware_stack(scope, receive, send)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\routing.py", line 774, in app
    await route.handle(scope, receive, send)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\routing.py", line 295, in handle
    await self.app(scope, receive, send)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "D:\devpinstall\anaconda3\envs\dbgpt_env\lib\site-packages\starlette\_exception_handler.py", line 75, in wrapped_app
    response = await handler(conn, exc)
  File "F:\pyproject\fastApiProject\errors\validation_error.py", line 16, in validationExceptionHandler
    return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder(http_resp.ResponseFail(errMsg)))
  File "F:\pyproject\fastApiProject\parameter\http_resp.py", line 24, in ResponseFail
    a=1/0
ZeroDivisionError: division by zero

3.2 自定义处理器

新建包 errors,并新增文件app_error.py,文件内容如下:

from fastapi import status
from fastapi.encoders import jsonable_encoder
from fastapi.requests import Request
from fastapi.responses import JSONResponse
from parameter import http_resp

async def appExceptionHandler(request: Request, exc: Exception):
    """自定义全局系统错误"""
    return JSONResponse(
        content=jsonable_encoder(http_resp.ResponseFail("系统运行异常,稍后重试~")),
        status_code=status.HTTP_200_OK,
    )

3.3 注册&覆盖

修改 errors/__init__.py文件中的统一注册方法registerCustomErrorHandle:

def registerCustomErrorHandle(server: FastAPI):
    """ 统一注册自定义错误处理器"""
    # 注册参数验证错误,并覆盖模式RequestValidationError
    server.add_exception_handler(RequestValidationError, validationExceptionHandler)
    # 错误处理StarletteHTTPException
    server.add_exception_handler(StarletteHTTPException, httpExceptionHandler)
    # 自定义全局系统错误
    server.add_exception_handler(Exception, appExceptionHandler)

3.4 验证

请求地址:http://localhost:8000/demo/resp/demo

参数:

{
        "name": "san",
        "age": 0,
        "phone": "一班",
        "likes" : ["足球","c"]
}

返回值:

{
  "code": 200,
  "msg": "处理成功",
  "data": "系统运行异常,稍后重试~"
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独行客-编码爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值