FastApi教程|处理错误

发表时间:2020-03-09

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

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

您可能需要告诉客户:

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

在这些情况下,通常会返回 400 (从400到499) 范围内 的 HTTP状态代码

这类似于200个HTTP状态代码(从200到299)。 这些“ 200”状态代码意味着请求中某种程度上存在“成功”。

400范围内的状态代码表示客户端出现错误。

还记得所有那些 “ 404 Not Found” 错误(和笑话)吗?

使用 HTTPException

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

导入 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]}

HTTPException 在您的代码中 引发

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

因为这是Python异常,所以您不是 return 它,而是 raise 它。

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

return 在有关依赖项和安全性的部分中 ,提出异常而 不是值 的好处 将更加明显。

在此示例中,当客户通过不存在的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]}

将得到的反应

如果客户端请求 http://example.com/items/foo item_id "foo" ),则他将收到HTTP状态代码200和JSON响应:

{
  "item": "The Foo Wrestlers"
}

但是,如果客户端请求 http://example.com/items/bar (不存在 item_id "bar" ),则他将收到404状态的HTTP状态代码(“未找到”错误)和JSON响应:

{
  "detail": "Item not found"
}

小费

如果要提高的 HTTPException ,你可以通过可转换为JSON作为参数的任何值 detail 不仅, str

你可以通过一个 dict ,一个 list ,等等。

它们由 FastAPI 自动处理 并转换为JSON。

添加自定义标题

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

安装自定义异常处理程序

您可以 从Starlette 使用 相同的异常实用 程序添加自定义异常处理 程序

假设您有一个自定义例外 UnicornException ,您(或您使用的库)可能会 例外 raise

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

您可以使用添加一个自定义异常处理程序 @app.exception_handler()

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}

在这里,如果您请求 /unicorns/yolo 路径操作 raise 一个 UnicornException

但是它将由 unicorn_exception_handler

因此,您将收到一个干净的错误,其HTTP状态代码为 418 ,JSON内容为:

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

技术细节

您也可以使用 from starlette.requests import Request from starlette.responses import JSONResponse

FastAPI 提供相同 starlette.responses fastapi.responses ,就像为你的方便,开发人员。 但是大多数可用的响应直接来自Starlette。 与相同 Request

覆盖默认的异常处理程序

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

这些处理器是主管或返回默认的JSON响应,当你 raise HTTPException 当请求无效数据和。

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

超越请求验证异常

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

它还包括一个默认的异常处理程序。

要覆盖它,请导入 RequestValidationError 并将其 @app.exception_handler(RequestValidationError) 用于装饰异常处理程序。

异常处理程序将收到 Request 和异常。

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

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 ,而不是使用以下命令获取默认的JSON错误:

{
    "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)

RequestValidationError VS ValidationError

警告

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

RequestValidationError 是Pydantic的子类 ValidationError

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

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

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

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

重写 HTTPException 错误处理程序

同样,您可以覆盖 HTTPException 处理程序。

例如,对于这些错误,您可能希望返回纯文本响应而不是JSON:

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

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}

技术细节

您也可以使用 from starlette.responses import PlainTextResponse

FastAPI 提供相同 starlette.responses fastapi.responses ,就像为你的方便,开发人员。 但是大多数可用的响应直接来自Starlette。

使用 RequestValidationError body

RequestValidationError 包含了 body 它收到无效数据。

您可以在开发应用程序时使用它来记录主体并对其进行调试,然后将其返回给用户,等等。

from fastapi import FastAPI, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel

app = FastAPI()


@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}),
    )


class Item(BaseModel):
    title: str
    size: int


@app.post("/items/")
async def create_item(item: Item):
    return item

现在尝试发送无效的项目,例如:

{
  "title": "towel",
  "size": "XL"
}

您将收到一条响应,告知您包含接收到的正文的数据无效:

{
  "detail": [
    {
      "loc": [
        "body",
        "item",
        "size"
      ],
      "msg": "value is not a valid integer",
      "type": "type_error.integer"
    }
  ],
  "body": {
    "title": "towel",
    "size": "XL"
  }
}

FastAPI HTTPException 与Starlette的 HTTPException

FastAPI 有其自己的 HTTPException

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

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

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

所以,你可以不断提高 FastAPI HTTPException 为常在你的代码。

但是,当您注册异常处理程序时,应将其注册为Starlette的 HTTPException

这样,如果Starlette内部代码的任何部分或Starlette扩展或插件产生了Starlette HTTPException ,您的处理程序将能够捕获并处理它。

在此示例中,为了使两个都具有 HTTPException 相同的代码,Starlette的异常被重命名为 StarletteHTTPException

from starlette.exceptions import HTTPException as StarletteHTTPException

重用 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}

在此示例中,您仅 print 通过一条非常具有表现力的消息来查看错误。

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

文章来源互联网,如有侵权,请联系管理员删除。邮箱:417803890@qq.com / QQ:417803890

微配音

Python Free

邮箱:417803890@qq.com
QQ:417803890

皖ICP备19001818号-4
© 2019 copyright www.pythonf.cn - All rights reserved

微信扫一扫关注公众号:

联系方式

Python Free