全面拥抱FastApi-优雅的处理HTTPException

在开发接口或者服务的时候,经常会遇到需要给客户端返回异常错误 例如:

  • 用户操作权限不够
  • 参数错误
  • 请求的资源不存在..

众所周知,因客户端或调用方的原因导致出错的,返回的状态码是以 4 开头的 (400~499)

比如常见的 404 Not Found, 资源不存在...

为了直观友好的给客户端返回错误, 在 FastApi 中一般使用 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]}

当遇到用户请求异常的时候,可以选择用 raise 将异常抛出去

抛出异常,便立即会结束本次请求,并将HTTP错误从HTTPException发送到客户端或浏览器

比如:在浏览器中输入 http://127.0.0.1:8000/items/jerry

由于 jerry 并不在 items 中,浏览器便会收到 404 以及一个 json 格式的 response

注意: 这个 json 由 FastAPI 自动处理并转换的。

自定义异常类

和 starlette 源码中处理异常一样,你也可以自定义一个异常处理类 定义的异常处理类,使用 @app.exception_handler() 支持在 FastAPI 中全局使用该异常类

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

在客户端就能收到一个提示友好,并事先定义好状态码 418 的提示错误

重写默认异常类

FastAPI 有许多的默认异常处理类

这些处理程序负责在引发 HTTPException 和请求包含无效数据时返回默认 JSON 响应

比如下面的路由是只支持 item_idint 类型的路径函数

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

当从客户端传过来的 item_id 为非 int 类型的时候,便返回默认的 JSON 响应

你可以重写这些默认的异常处理类,变成自定义的。比如

重写请求验证异常类

当一个请求包含非法的请求数据时,会触发 FastAPI 中的 RequestValidationError

为了重写该异常处理类,需要导入 RequestValidationError, 并使用 @app.exception_handler(RequestValidationError) 对异常处理函数进行装饰

from fastapi.exceptions import RequestValidationError

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
    return PlainTextResponse(str(exc), status_code=400)

将这部分代码,和上面的代码合并后运行。再次请求会看到不一样的提示~~~

重写 HTTPException

同样,你也可以重写 HTTPException 处理程序

比如你想返回文本的错误提示,而不是默认的 JSON 格式错误提示

和上面一样,使用 @app.exception_handler(HTTPException) 装饰异常处理函数即可

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.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}

返回异常请求body

当接收到非法请求的时候,RequestValidationError 中包含异常请求体的,只是没有给我们返回

但是在开发应用程序或者与前端联调的时候,可以将请求体加到返回的 response

这样在出现问题的时候,可以通过日志或响应,快速定位到问题!

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"
}

对于异常请求,收到的响应中就会包含该次异常请求的请求 body

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

在联调或开发的时候,可以节省一些不必要的时间,提高效率!

推荐阅读

![在这里插入图片描述](https://img-blog.csdnimg.cn/20201231154552503.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NwaWRlcl9weQ==,size_16,color_FFFFFF,t_70#pic_center)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

全村之希望

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

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

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

打赏作者

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

抵扣说明:

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

余额充值