我使用FastAPI和Pydantic。
我的问题-我需要使用Pydantic引发ValueError
from fastapi import FastAPI
from pydantic import BaseModel, validator
from fastapi import Depends, HTTPException
app = FastAPI()
class RankInput(BaseModel):
rank: int
@validator('rank')
def check_if_value_in_range(cls, v):
"""
check if input rank is within range
"""
if not 0 < v < 1000001:
raise ValueError("Rank Value Must be within range (0,1000000)")
#raise HTTPException(status_code=400, detail="Rank Value Error") - this works But I am looking for a solution using ValueError
return v
def get_info_by_rank(rank):
return rank
@app.get('/rank/{rank}')
async def get_rank(value: RankInput = Depends()):
result = get_info_by_rank(value.rank)
return result
这段代码在引发ValueError时给出Internal Server Error
INFO: 127.0.0.1:59427 - "GET /info/?rank=-1 HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/uvicorn/protocols/http/h11_impl.py", line 396, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/fastapi/applications.py", line 199, in __call__
await super().__call__(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/routing.py", line 566, in __call__
await route.handle(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/routing.py", line 41, in app
response = await func(request)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/fastapi/routing.py", line 195, in app
dependency_overrides_provider=dependency_overrides_provider,
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/fastapi/dependencies/utils.py", line 550, in solve_dependencies
solved = await run_in_threadpool(call, **sub_values)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/concurrency.py", line 34, in run_in_threadpool
return await loop.run_in_executor(None, func, *args)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "pydantic/main.py", line 400, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for GetInput
rank
ValueError() takes no keyword arguments (type=type_error)
ERROR:uvicorn.error:Exception in ASGI application
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/uvicorn/protocols/http/h11_impl.py", line 396, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/fastapi/applications.py", line 199, in __call__
await super().__call__(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/routing.py", line 566, in __call__
await route.handle(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/routing.py", line 41, in app
response = await func(request)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/fastapi/routing.py", line 195, in app
dependency_overrides_provider=dependency_overrides_provider,
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/fastapi/dependencies/utils.py", line 550, in solve_dependencies
solved = await run_in_threadpool(call, **sub_values)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/starlette/concurrency.py", line 34, in run_in_threadpool
return await loop.run_in_executor(None, func, *args)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "pydantic/main.py", line 400, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for GetInput
rank
ValueError() takes no keyword arguments (type=type_error)
我也查了https://github.com/tiangolo/fastapi/issues/2180。
但我没能想出解决办法。
我需要做的是用自定义状态码提升ValueError
。
注意-我知道我可以通过提高HTTPException
来完成任务。
但我正在寻找使用ValueError
的解决方案
你能告诉我我哪里做错了吗?
此问题也发布在Github -https://github.com/tiangolo/fastapi/issues/3761
3条答案
按热度按时间ie3xauqp1#
如果你没有抛出一个
HTTPException
,那么任何其他未捕获的异常通常都会生成一个500响应(一个Internal Server Error
)。如果你的意图是在引发特定异常时使用其他自定义错误消息和HTTP状态进行响应-比如ValueError
-那么你可以使用添加全局异常处理程序到你的应用中:这将给予一个400响应(或者您可以将状态代码更改为您喜欢的任何代码),如下所示:
hsvhsicv2#
请注意,
pydantic
期望验证器产生ValueError
、TypeError
或AssertionError
(参见docs),pydantic
将其转换为ValidationError
。此外,根据FastAPI的文档:
当请求包含无效数据时,FastAPI会在内部引发
RequestValidationError
。和
RequestValidationError
是Pydantic的ValidationError
的子类。这样做的结果是,在
pydantic
的Model验证期间引发的标准Validation
错误将被转换为422 Unprocessable Entity
,并且响应主体将包含验证失败的详细原因。(As旁注:
pydantic
带有约束类型,允许约束基本数据类型,而无需编写显式验证器。如果上面的内容不令人满意,并且您希望更改行为,下面是我的方法(有关
ValidationError
处理的详细信息,请参阅此处):对端点的调用现在给出:
如果你要添加一个使用相同模型的不同端点,异常处理程序也会自动处理这个问题,例如:
如果这不是你要找的,你能解释一下你为什么要提出一个
ValueError
吗?wrrgggsh3#
您遇到了FastAPI bug/讨论:https://github.com/tiangolo/fastapi/issues/1474
你可能会期望,如果Pydantic模型在
Depends()
中引发ValidationError
,那么FastAPI会将其转换为RequestValidationError
,就像它在请求主体的模型中遇到一个一样。但事实并非如此这就是窃听器的作用。上面链接的bug中有一些解决方案。
我更喜欢的一个稍微简单一点的选择是添加一个类方法来捕获
ValidationError
并将其 Package 到RequestValidationError
中,就像Pydantic对请求体所做的那样:我肯定不会***做的是为
ValueError
或ValidationError
实现自定义错误处理程序,或者返回自定义JSONResponse
而不是引发异常。正如FastAPI文档所说:如果你在响应中或代码中的任何地方(而不是在客户端的请求中)有一个Pydantic ValidationError,它实际上是代码中的一个bug。
这也可以说适用于
ValueError
。返回自定义
JSONResponse
的问题是,您必须完成所有工作,以确保它遵循与FastAPI从RequestValidationError
生成的422响应完全相同的格式。