错误提示是非常重要的,比如权限控制、资源控制等场景,需要服务器返回给用户错误提示,包括状态码和提示等内容,所以本文就主要记录FastAPI里的错误处理相关的内容

抛出一个HTTPException

@app08.get("/stu08/{id}")
def stu08_get_error(
        id: str = Path(...)
):
    if id != "MinChess":
        return HTTPException(status_code=404, detail="not found")
    return {
        "name": "MinChess",
        "age": 22,
        "Blog": "https://blog.jiumoz.com"
    }

fastapi中导入HTTPException,并在路径操作函数中进行判断输出即可;

上面的代码就是当输入的id不为MinChess的时候抛出错误,错误代码为404,详细信息为not found

image-20221203164124803

添加自定义响应头

@app08.get("/stu08/header/{id}")
def stu08_get_error_header(
        id: str
):
    if id != "MinChess":
        raise HTTPException(
            status_code=404,
            detail="Not Found",
            headers={"X-Error": "There gose my error."}
        )
    return {
        "name": "MinChess",
        "age": 22,
        "Blog": "https://blog.jiumoz.com"
    }

有些场景的前端可能需要在HTTP的错误中添加一些自定义的响应头,添加方法如上,直接设置HTTPExceptionheaders即可;

image-20221203164434584

自定义异常处理器

定义一个Exception类型错误类UnicornException,在实际应用中就对应登录失败错误、验证码错误等;

class UnicornException(Exception):
    def __init__(self, name: str):
        self.name = name

在主应用中为UnicornException错误定义处理函数,如果没在同一个文件内,需要导入对应的类,参考文末的源码;

@app.exception_handler(exception.UnicornException) # 用@app.exception_handler() 为UnicornException添加自定义异常控制器
async def unicorn_exception_handler(request: Request, exc: exception.UnicornException):
    """
    :param request: 请求头
    :param exc: 异常对象
    :return:
    """
    print(f"全局自定义异常:method:{request.method} URL:{request.url}Headers:{request.headers}")
    return JSONResponse(
        status_code=418,
        content={
            "message": f"你输入的: {exc.name} 是错的!!!"
        }
    )

在应用中进行声明即可,要导入相关的类;

@app08.get("/stu08/custom/{name}")
async def stu08_custom(
        name: str = Path(default="MinChess")
):
    if name == "MinChess":
        raise UnicornException(name=name)
    return {"name": "MinChess"}

上面的函数就是当用户输入的name为MinChess的时候,抛出一个UnicornException错误;这里的错误处理函数要实现全局处理的话,就一定要挂载道主应用上;

根据上面的处理函数,我们操作输入MinChess的时候,不仅会返回给前端既定的数据,也会在控制台进行相关信息的输出,如下:

image-20221203164558523

image-20221203164641276

重写默认异常处理器

重写默认的异常处理器就是将fastapi内置的默认异常进行人为干预,方法很简单,同上面的方法,进行重写即可;

@app.exception_handler(RequestValidationError)  # 重写请求验证异常处理器
async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
    """
    请求参数验证异常
    :param request: 请求头信息
    :param exc: 异常对象
    :return:
    """
    # 日志记录异常详细上下文
    print(f"全局异常:{request.method}URL{request.url}Headers:{request.headers}{traceback.format_exc()}")
    return PlainTextResponse(str(exc), status_code=400)


@app.exception_handler(StarletteHTTPException)  # 重写HTTPException异常处理器
async def http_exception_handler(request, exc):
    print(f"全局异常:{request.method}URL{request.url}Headers:{request.headers}{traceback.format_exc()}")
    return PlainTextResponse(str(exc.detail), status_code=exc.status_code)

上面的第一个函数就是重写的参数校验的处理器,返回PlainTextResponse信息的同时会在控制台打印具体错误;

上面的第二个函数就是重写的HTTPException异常的处理器,同样返回PlainTextResponse信息的同时会在控制台打印具体错误;

  • 参数校验

这部分不需要重新写操作函数,找到之前写的某一个请求,发送错误类型的错误就会抛出异常:

image-20221203170053064

image-20221203170120577

image-20221203165215816

  • HTTPException
@app08.get("/stu08/Request_Validation_Error/{param}")
async def stu08_request_validation_error(param: int):
    if param != 2:
        raise HTTPException(detail="请输入MinChess!", status_code=410)
    return {"param": param}

新建一个请求,用本章第一个例子也可以,上面的代码就表示当输入的param不为2时,抛出HTTPException异常:

image-20221203164912237

image-20221203164845563

源码

项目结构如下图,具体的配置可以参看下面文章的第二节:

https://blog.jiumoz.com/archives/fastapi-cong-ru-men-dao-shi-zhan-13-chang-jian-pei-zhi-xiang

image-20221203163907997

# -*- coding: utf-8 -*-
# @Time: 2022/12/2 16:28
# @Author: MinChess
# @File: exception.py
# @Software: PyCharm
class UnicornException(Exception):
    def __init__(self, name: str):
        self.name = name
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, PlainTextResponse, HTMLResponse
from fastapi.exceptions import RequestValidationError
from fastapi.staticfiles import StaticFiles
from stu import app01, app02, app03, app04, app05, app06, app07, app08
import uvicorn
import traceback
from utils import exception # 导入类
from starlette.exceptions import HTTPException as StarletteHTTPException

app = FastAPI(
    # 创建一个FastAPI实例\这里的变量 app 会是 FastAPI 类的一个「实例」。\这个实例将是创建你所有 API 的主要交互对象。\这个 app 同样在命令中被 uvicorn 所引用:
    title='FastAPI学习教程文档——title',
    description='这是FastAPI教程的文档——description',
    version='1.0.0',
    docs_url='/docs',
    redoc_url='/redoc',
)

app.mount(path="/static", app=StaticFiles(directory='./static'), name='static')


@app.exception_handler(exception.UnicornException) # 用@app.exception_handler() 为UnicornException添加自定义异常控制器
async def unicorn_exception_handler(request: Request, exc: exception.UnicornException):
    """
    :param request: 请求头
    :param exc: 异常对象
    :return:
    """
    print(f"全局自定义异常:method:{request.method} URL:{request.url}Headers:{request.headers}")
    return JSONResponse(
        status_code=418,
        content={
            "message": f"你输入的: {exc.name} 是错的!!!"
        }
    )


@app.exception_handler(RequestValidationError)  # 重写请求验证异常处理器
async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
    """
    请求参数验证异常
    :param request: 请求头信息
    :param exc: 异常对象
    :return:
    """
    # 日志记录异常详细上下文
    print(f"全局异常:{request.method}URL{request.url}Headers:{request.headers}{traceback.format_exc()}")
    return PlainTextResponse(str(exc), status_code=400)


@app.exception_handler(StarletteHTTPException)  # 重写HTTPException异常处理器
async def http_exception_handler(request, exc):
    print(f"全局异常:{request.method}URL{request.url}Headers:{request.headers}{traceback.format_exc()}")
    return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
# -*- coding: utf-8 -*-
# @Time: 2022/12/1 14:55
# @Author: MinChess
# @File: stu08.py
# @Software: PyCharm
from fastapi import APIRouter, HTTPException, Path,Body
from fastapi.exceptions import RequestValidationError
from fastapi.encoders import jsonable_encoder
from typing import Optional, List
from pydantic import Field
from utils import UnicornException
from pydantic import BaseModel
from datetime import datetime

app08 = APIRouter()


# 抛出HTTPException
@app08.get("/stu08/{id}")
def stu08_get_error(
        id: str = Path(...)
):
    if id != "MinChess":
        return HTTPException(status_code=404, detail="not found")
    return {
        "name": "MinChess",
        "age": 22,
        "Blog": "https://blog.jiumoz.com"
    }


# 添加自定义响应头
@app08.get("/stu08/header/{id}")
def stu08_get_error_header(
        id: str
):
    if id != "MinChess":
        raise HTTPException(
            status_code=404,
            detail="Not Found",
            headers={"X-Error": "There gose my error."}
        )
    return {
        "name": "MinChess",
        "age": 22,
        "Blog": "https://blog.jiumoz.com"
    }


# Theere id MinChess's python pro
# 自定义异常处理器

@app08.get("/stu08/custom/{name}")
async def stu08_custom(
        name: str = Path(default="MinChess")
):
    if name == "MinChess":
        raise UnicornException(name=name)
    return {"name": "MinChess"}


@app08.get("/stu08/Request_Validation_Error/{param}")
async def stu08_request_validation_error(param: int):
    if param != 2:
        raise HTTPException(detail="请输入MinChess!", status_code=410)
    return {"param": param}


感谢阅读!

九陌斋地址:https://blog.jiumoz.com/archives/fastapi-cong-ru-men-dao-shi-zhan-12-cuo-wu-chu-li

欢 迎 关 注 博 主 个 人 小 程 序!