Raj*_*eer 3 python python-3.x pydantic fastapi
我的路线:
@router.get('/check/{value}', status_code=200)
def ranks_check(value: BasicInput = Depends()):
"""
Test endpoint
"""
return value
Run Code Online (Sandbox Code Playgroud)
我的型号:
class BasicInput:
"""
Get Confidence to score Input class
"""
value: int
@validator('value')
def check_if_value_in_range(cls, v):
if not 0 < v < 1000001:
raise ValueError('Value Exceeded Limit')
Run Code Online (Sandbox Code Playgroud)
我需要做什么:
我需要验证输入并在出现 ValueError 时引发 HTTP 400。
我知道我可以使用 Pydantic 的类型完成整数验证,并在路由函数本身中Field进行运行。check_if_value_in_range我正在寻找使用该模型的解决方案。
请参阅 FastAPI 文档中有关在代码中引发 HTTPException 的内容:
HTTPException是一个普通的 Python 异常,带有与 API 相关的附加数据。因为它是 Python 异常,所以你不
return它,你raise它。这也意味着,如果您位于在路径操作函数内部调用的实用程序函数内,并且从该实用程序函数内部引发,则它不会运行路径操作函数
HTTPException中的其余代码,它将立即终止该请求并将 HTTP 错误从 发送到客户端。HTTPException
from fastapi.exceptions import HTTPException
class BasicInput(BaseModel):
value: int
@validator("value")
def check_if_value_in_range(cls, v):
if not 0 < v < 1000001:
# raise ValueError("Value Exceeded Limit")
raise HTTPException(status_code=400, detail="value exceeded limit")
return v
Run Code Online (Sandbox Code Playgroud)
from fastapi.exceptions import HTTPException
class BasicInput(BaseModel):
value: int
@validator("value")
def check_if_value_in_range(cls, v):
if not 0 < v < 1000001:
# raise ValueError("Value Exceeded Limit")
raise HTTPException(status_code=400, detail="value exceeded limit")
return v
Run Code Online (Sandbox Code Playgroud)
但要使其发挥作用,当前代码中存在一些问题需要修复:
如果您使用 Pydantic 的验证器装饰器,那么您的类需要是 PydanticBaseModel或Pydanticdataclass。
class BasicInput(BaseModel): # <--------------------
value: int
Run Code Online (Sandbox Code Playgroud)
如果您还在函数print上添加语句或断点validator,您会发现它实际上从未被调用,因为它不是 Pydantic BaseModel。
验证器函数缺少对条件if为False(当v在范围内有效时)的情况的处理。来自 Pydantic 文档:
验证器应该返回解析的值或引发
ValueError、TypeError或AssertionError(assert可以使用语句)。
class BasicInput(BaseModel):
value: int
@validator("value")
def check_if_value_in_range(cls, v):
if not 0 < v < 1000001:
raise ValueError("Value Exceeded Limit")
# v is good
return v # <--------------------
Run Code Online (Sandbox Code Playgroud)
在您的原始代码中,您提到您没有得到“什么”。我假设您收到的{}有效和无效响应均为空value:
$ curl -i -XGET localhost:8000/check/1000002
HTTP/1.1 400 Bad Request
...
{"detail":"value exceeded limit"}
$ curl -i -XGET localhost:8000/check/42
HTTP/1.1 200 OK
...
{"value":42}
Run Code Online (Sandbox Code Playgroud)
这是因为在路由函数中,value是BasicInput类,它不是{value}路径值或BasicInput.value整数值。
@router.get("/check/{value}", status_code=200)
def ranks_check(value: BasicInput = Depends()):
print(type(value)) # <class 'main.BasicInput'>
return value
Run Code Online (Sandbox Code Playgroud)
您实际上得到的“空”响应是 FastAPI 将其jsonable_encoder应用于该类的结果BasicInput。当 FastAPI 到达return value您的路由函数时,它将用于jsonable_encoder将其转换为JSONResponse. 请参阅直接返回响应的文档。
在内部,由于BasicInput不是 PydanticBaseModel或可迭代对象(例如类似dict对象),因此它会导致空 dict {}。(您可以检查的代码jsonable_encoder,但这是因为dict(obj)和vars(obj))。
因此,正如MatsLindh 的回答中提到的,FastAPI 请求/响应上下文中使用的模型通常是 Pydantic 的子类BaseModel,在正确子类化之后,您现在应该得到非空响应:
class BasicInput(BaseModel): # <--------------------
value: int
Run Code Online (Sandbox Code Playgroud)
修复此问题后,只需按照 FastAPI 的在代码中引发 HTTPException 中的指南(正如我在本答案开头提到的那样)即可直接从模型引发 HTTP 错误。
但我同意MatsLindh 的评论,这不是一个“好的”做法,因为它违反了SRP /单一责任原则。您的模型正在做两件事:验证您的输入并引发适当的 HTTP 错误响应。
FastAPI 应该处理您的请求和响应,而 Pydantic 应该代表您的模型和数据。您让 FastAPI 接受请求,将其传递给 Pydantic 以验证并存储模型数据,然后让 FastAPI 将结果转换为适当的响应。您预期的解决方案也令人困惑,因为对于有效/成功的情况,它是在路由函数中处理的,但对于无效/错误的情况,它是在模型验证函数中处理的。
FastAPI 已经知道如何捕获 PydanticValidationError并将其转换为适当的 HTTP 错误响应。在这种情况下,FastAPI 返回 HTTP 500 内部服务器错误。
如果目的是提供特定于模型的错误响应,您应该raise ValueError在模型上保留 ,然后覆盖 FastAPI 的默认验证错误处理程序。请参阅“覆盖默认异常处理程序”部分。
这是我推荐的代码:
from fastapi import APIRouter, Depends, FastAPI
from fastapi.exceptions import ValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel, validator
# MODELS
class BasicInput(BaseModel):
value: int
@validator("value")
def check_if_value_in_range(cls, v):
if not 0 < v < 1000001:
raise ValueError("Value Exceeded Limit")
return v
# VIEWS
api = FastAPI()
router = APIRouter()
@api.exception_handler(ValidationError)
async def validation_exception_handler(request, exc: ValidationError):
return JSONResponse(status_code=400, content={"error": str(exc)})
@router.get("/check/{value}")
def ranks_check(value: BasicInput = Depends()):
return value
api.include_router(router)
Run Code Online (Sandbox Code Playgroud)
class BasicInput(BaseModel):
value: int
@validator("value")
def check_if_value_in_range(cls, v):
if not 0 < v < 1000001:
raise ValueError("Value Exceeded Limit")
# v is good
return v # <--------------------
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4231 次 |
| 最近记录: |