全面拥抱FastApi —优雅的返回异常错误
在开发接口或者服务的时候,经常会遇到需要给客户端返回异常错误 例如:\
- 用户操作权限不够
- 参数错误
- 请求的资源不存在…
众所周知,因客户端或调用方的原因导致出错的,返回的状态码是以 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_id
为 int
类型的路径函数
@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"
}
}
在联调或开发的时候,可以节省一些不必要的时间,提高效率!
推荐阅读
\
- 点赞
- 收藏
- 关注作者
评论(0)