FastAPI中全局异常处理

装饰器版本自定义异常

python 复制代码
1.首先我们定义三个文件,分别为exception.py,main.py, user.py

2.自定义异常需要继承HTTPException,该异常可以从fastapi中直接导入
from fastapi import HTTPException

3.exception.py中定义我们业务模块的异常
from fastapi import HTTPException

class UserDoesNotExistsException(HTTPException):
  def __init__(self, detail: str, status_code: int):
    self.detail = detail
    self.status_code = status_code

4. user.py文件
router_user = APIRouter(prefix='/user', tags=['用户模块'])
@router_user.get('/{user_id}')
async def get_id_by_user(user_id: int):
    if user_id != 1:
        raise UserDoesNotExistsException(status_code=400, detail="id not exists")
    return {"user_id": user_id}

5.main.py文件
from fastapi import FastAPI
from exception import UserDoesNotExistsException
from user import router_user

app = FastAPI(debug=True)

# 这里就是添加使用我们自定义的错误处理
@app.exception_handler(UserDoesNotExistsException)
def user_exception_handler(req: Request, ex: UserDoesNotException):
    return JSONResponse(
        status_code=ex.status_code,
        content={"message": f'error: {ex.detail}'}
    )

app.include_router(router_user, prefix='/api/v1')

非装饰器版

第一种,通过在FastAPI()中指定exception_handlers

python 复制代码
from fastapi import FastAPI
from fastapi.responses import JSONResponse

async def exception_not_found(request, exc):
    return JSONResponse({
        'code':exc.status_code,
        'error':'not found',
        status_code=exc.status_code
    })

exception_handlers = {
    # 键值对的形式,写具体的状态码或者具体的Exception子类都可以,后面完整例子中有
    404: exception_not_found,
}

app = FastAPI(exception_handlers=exception_handlers)

第二种,通过实例化的FastAPI对象的add_exception_handler方法

python 复制代码
from fastapi import FastAPI
from fastapi.responses import JSONResponse

async def exception_not_found(request, exc):
    return JSONResponse({
        'code':exc.status_code,
        'error':'not found',
        status_code=exc.status_code
    })

app = FastAPI()
# 同理,可以写具体的状态码或者具体的Exception子类都可以
app.add_exception_handler(404, exception_not_found)

完整案例,项目中可以使用

python 复制代码
1.定义四个文件,exception.py(全局处理), main.py(主程序文件), user/user.py(业务模块), user/exception.py(用户模块自己的错误处理)

2.exception.py文件
from fastapi import HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

# 全局异常       
async def global_exception_handler(request, exc):
    if exc.status_code == 500:
        err_msg = 'Server Internal Error'
    else:
        err_msg = exc.detail
    return JSONResponse({
        'code': exc.status_code,
        'err_msg': err_msg,
        'status': 'Failed'
    })

# 请求数据无效时的错误处理
""" 
example: http://127.0.0.1/user/{user_id}
success: http://127.0.0.1/user/1
failed: http://127.0.0.1/user/d
"""
async def validate_exception_handler(request, exc):
    err = exc.errors()[0]
    return JSONResponse({
        'code': 400,
        'err_msg': err['msg'],
        'status': 'Failed'
    })

golbal_exception_handlers = {
    HTTPException: global_exception_handler,
    RequestValidationError: validate_exception_handler
}

class BaseAPIException(HTTPException):
    status_code = 400
    detail = 'api error'
    def __init__(self, detail: str = None, status_code: int = None):
        self.detail = detail or self.detail
        self.status_code = status_code or self.status_code

3.定义user/exception.py
from exception import BaseAPIException

class UserDoesNotExistsException(BaseAPIException):
    status_code = 10000
    detail = 'user does not exists'

4.定义uers/user.py
from fastapi.routing import APIRouter

from .exception import UserDoesNotExistsException

router_user = APIRouter(prefix='/user', tags=['用户模块'])

@router_user.get("/{user_id}")
async def get_id_by_user(user_id: int):
    if user_id != 1:
        # 这里使用我们自定义的用户错误处理
        # 返回的统一响应格式{"code":10000,"err_msg":"user does not exists","status":"Failed"}
        raise UserDoesNotExistsException
    return {"user_id": user_id}

5.定义main.py
from fastapi import FastAPI
from exception import golbal_exception_handlers
from user.user import router_user

app = FastAPI(debug=True, exception_handlers=golbal_exception_handlers)

app.include_router(router_user, prefix='/api/v1')


if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app='main:app', host='0.0.0.0', port=9002, reload=True)

6.响应
# example: http://127.0.0.1:9002/api/v1/user/2
{"code":10000,"err_msg":"user does not exists","status":"Failed"}
# example: http://127.0.0.1:9002/api/v1/user/d
{"code":400,"err_msg":"value is not a valid integer","status":"Failed"}