首页>文档>FastAPI>fastapi中文手册:Handling Errors(处理错误)

fastapi中文手册:Handling Errors(处理错误)

fastapi中文手册:Handling Errors(处理错误)

FastAPI 是一个高性能 Web 框架,用于构建 API。

主要特性:

  • 快速:非常高的性能,与 NodeJS 和 Go 相当
  • 快速编码:将功能开发速度提高约 200% 至 300%
  • 更少的错误:减少约 40% 的人为错误
  • 直观:强大的编辑器支持,自动补全无处不在,调试时间更少
  • 简易:旨在易于使用和学习,减少阅读文档的时间。
  • 简短:减少代码重复。
  • 稳健:获取可用于生产环境的代码,具有自动交互式文档
  • 基于标准:基于并完全兼容 API 的开放标准 OpenAPI 和 JSON Schema 

在许多情况下,您需要将错误通知给使用API的客户端。

该客户端可以是带有前端的浏览器,其他人的代码,IoT设备等。

你需要告诉客户端:

  • 没有足够的权限进行该操作。
  • 无权访问该资源。
  • 尝试访问的项目不存在。
    等等

这种情况下,你通常需要返回一个400-499之间的 HTTP状态码

400-499的状态码说明客户端出现了错误。

一、使用 HTTPException

要将带有错误的HTTP响应返回给客户端,请使用HTTPException

1.1. Import HTTPException

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]}
1.2. 抛出 HTTPException 错误

HTTPException 一个普通的Python异常,其中包含与API相关的其他数据。

因为它是Python的异常,你不是return,而是raise它。

这也意味着,如果您在要在path操作函数内部调用的实用程序函数内部,并且从该实用程序函数内部引发HTTPException,它将不会在path操作中运行其余代码 函数,它将立即终止该请求并将HTTP错误从HTTPException发送到客户端。

下面这个例子,客户端请求一个不存在的ID的内容,将会抛出一个404异常:

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]}
1.3. 产生的响应

客户端请求 http://example.com/items/foo (an item_id "foo"), 客户端将接受到一个200的状态码,然后返回的JSON如下:

{
  "item": "The Foo Wrestlers"
}

但是,如果客户端请求 http://example.com/items/bar (a non-existent item_id "bar"), 它将会返回一个404的状态码,返回的JSON数据如下:

{
  "detail": "Item not found"
}

提示

当抛出 HTTPException 异常时, 你可以传递任何能转换为JSON的值,作为异常的详细描述,不仅仅是`str,可以传递一个字典、列表等。

二、添加自定义的headers

在某些情况下,能够将自定义标头添加到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]}

三、使用自定义的异常handlers

You can add custom exception handlers with the same exception utilities from Starlette.

假如,你需要抛出自定义的异常UnicornException

您想使用FastAPI全局处理此异常。

你可以添加一个自定义异常handler通过 @app.exception_handler():

from fastapi import FastAPI
from starlette.requests import Request
from starlette.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处理.

因此,你将收到一个明显的提示,状态码是418,并且JSON的内容如下:

{"message": "Oops! yolo did something. There goes a rainbow..."}

四、覆盖默认的异常处理程序

FastAPI 有一些默认的异常处理程序。

当您引发HTTPException且请求中包含无效数据时,这些处理程序将负责或返回默认的JSON响应。

您可以使用自己的异常处理程序覆盖它们。

4.1. 覆盖请求验证的异常

当请求包含无效数据时,** FastAPI **内部会引发RequestValidationError

并且它也包含了默认的异常处理。

想要覆盖它,需要导入 RequestValidationError 并且使用 @app.exception_handler(RequestValidationError) 去装饰这个异常处理器.

异常处理程序将收到一响应和异常。

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError 
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.responses import PlainTextResponse

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 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)
4.1.1 RequestValidationError vs ValidationError

这些技术细节如果现在对您不重要,则可以跳过。

RequestValidationError是Pydantic的[ValidationError`](https://pydantic-docs.helpmanual.io/#error-handling)的子类。

** FastAPI **的用法是,如果您在response_model中使用Pydantic模型,并且您的数据有错误,您将在日志中看到该错误。

但是客户端/用户将看不到它。 相反,客户端将收到带有HTTP状态代码“ 500”的“内部服务器错误”。

之所以应该这样,是因为如果您的响应或代码中任何地方(而不是客户端的请求)中都有PydanticValidationError,则实际上是代码中的错误。

而且,在修复该错误时,您的客户/用户不应访问有关该错误的内部信息,因为这可能会暴露一个安全漏洞。

4.2. 覆盖 HTTPException 异常处理程序

同样你也可以覆盖HTTPException异常:

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException 
from starlette.responses import PlainTextResponse

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 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}
4.2.1 FastAPI’s HTTPException vs Starlette’s HTTPException

FastAPI 有自己的 HTTPException.

FastAPI **的HTTPException错误类继承自Starlette的HTTPException错误类。

唯一的区别是** FastAPI **的HTTPException允许您添加要包含在响应中的标头。

OAuth 2.0和某些安全实用程序在内部需要/使用此功能。

因此,您可以像平常在代码中一样继续提高** FastAPI **的HTTPException。

但是当您注册异常处理程序时,应该为Starlette的HTTPException注册它。

这样,如果Starlette内部代码的任何部分,或者Starlette扩展或插件引发了HTTPException,则您的处理程序将能够捕获该异常。

在此示例中,为了能够将两个HTTPException都包含在同一代码中,Starlette的异常被重命名为StarletteHTTPException:

from starlette.exceptions import HTTPException as StarletteHTTPException

4.3. 重用** FastAPI **的异常处理程序

您也可能只想以某种方式使用该异常,然后使用** FastAPI **中相同的默认异常处理程序。

您可以从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}

在此示例中,您只是使用非常明确的通知打印错误。

但是您知道了,可以使用异常,然后重新使用默认的异常处理程序。

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索