简体   繁体   English

FastAPI - Pydantic - 值错误引发内部服务器错误

[英]FastAPI - Pydantic - Value Error Raises Internal Server Error

I am using FastAPI with Pydantic.我将 FastAPI 与 Pydantic 一起使用。

My problem - I need to raise ValueError using 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

this piece of code gives Internal Server Error when a ValueError is raised当引发 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)

I also checked https://github.com/tiangolo/fastapi/issues/2180 .我还检查了https://github.com/tiangolo/fastapi/issues/2180

But I was not able to figure out a solution.但我无法找到解决方案。

What I need to do is Raise ValueError with a Custom Status Code.我需要做的是使用自定义状态代码ValueError

Note - I know I can get the Job Done by raising HTTPException .注意 - 我知道我可以通过提高HTTPException来完成工作。

But I am looking for a solution using ValueError但我正在寻找使用ValueError的解决方案

Could you tell me where I am going wrong?你能告诉我哪里出错了吗?

Have Also Posted this Issue on Github - https://github.com/tiangolo/fastapi/issues/3761在 Github 上也发布了这个问题 - https://github.com/tiangolo/fastapi/issues/3761

If you're not raising an HTTPException then normally any other uncaught exception will generate a 500 response (an Internal Server Error ).如果您没有引发HTTPException ,那么通常任何其他未捕获的异常都会生成 500 响应( Internal Server Error )。 If your intent is to respond with some other custom error message and HTTP status when raising a particular exception - say, ValueError - then you can use add a global exception handler to your app:如果您的意图是在引发特定异常(例如ValueError )时以其他一些自定义错误消息和 HTTP 状态进行响应,那么您可以使用将全局异常处理程序添加到您的应用程序:

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse


@app.exception_handler(ValueError)
async def value_error_exception_handler(request: Request, exc: ValueError):
    return JSONResponse(
        status_code=400,
        content={"message": str(exc)},
    )

This will give a 400 response (or you can change the status code to whatever you like) like this:这将给出 400 响应(或者您可以将状态代码更改为您喜欢的任何内容),如下所示:

{
    "message": "Value Must be within range (0,1000000)"
}

Please note that pydantic expects that validators raise a ValueError , TypeError , or AssertionError (see docs ) which pydantic will convert into a ValidationError .请注意, pydantic期望验证器引发ValueErrorTypeErrorAssertionError (请参阅docs ), pydantic会将其转换为ValidationError

Further, as per FastAPI's documentation :此外,根据FastAPI 的文档

When a request contains invalid data, FastAPI internally raises a RequestValidationError .当请求包含无效数据时,FastAPI 会在内部引发RequestValidationError

and

RequestValidationError is a sub-class of Pydantic's ValidationError . RequestValidationError是 Pydantic 的ValidationError的子类。

The result of this is that a standard Validation error raised during pydantic 's Model validation will be translated into a 422 Unprocessable Entity , and the response body will contain details on why the validation failed.这样做的结果是,在pydantic的模型验证期间引发的标准Validation错误将被转换为422 Unprocessable Entity ,并且响应正文将包含有关验证失败原因的详细信息。

(As a side note: pydantic comes with constrained types which allow to constrain basic datatypes without having to write explicit validators.) (附带说明: pydantic带有约束类型,允许约束基本数据类型而无需编写显式验证器。)

If the above is not satisfactory and you'd like to change the behaviour, here's how I would approach it (see here for details on the ValidationError handling):如果上述情况不令人满意并且您想更改行为,那么我将采用以下方法(有关ValidationError处理的详细信息,请参见此处):

from fastapi import Depends, FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.exception_handlers import request_validation_exception_handler
from fastapi.responses import JSONResponse
from pydantic import BaseModel, conint

class RankInput(BaseModel):
    # Constrained integer, must be greater that or equal to 0
    # and less than or equal to 1 million.
    rank: conint(ge=0, le=1_000_000)

async def rank_out_of_bound_handler(request: Request, exc: RequestValidationError):

    validation_errors = exc.errors()
    for err in validation_errors:
        # You could check for other things here as well, e.g. the error type.
        if "rank" in err["loc"]:
            return JSONResponse(
                status_code=status.HTTP_400_BAD_REQUEST,
                content={"message": "Rank must be in range [0, 1000000]."}
            )

    # Default response in every other case.
    return await request_validation_exception_handler(request, exc)

def get_info_by_rank(rank):
    return rank


app = FastAPI(
    exception_handlers={RequestValidationError: rank_out_of_bound_handler},
)

@app.get('/rank/{rank}')
async def get_rank(value: RankInput = Depends()):
    result = get_info_by_rank(value.rank)
    return result

A call to the endpoint now gives:对端点的调用现在给出:

$ curl -i "http://127.0.0.1:8000/rank/1"
HTTP/1.1 200 OK
date: Sat, 28 Aug 2021 20:47:58 GMT
server: uvicorn
content-length: 1
content-type: application/json

1
$  curl -i "http://127.0.0.1:8000/rank/-1"
HTTP/1.1 400 Bad Request
date: Sat, 28 Aug 2021 20:48:24 GMT
server: uvicorn
content-length: 49
content-type: application/json

{"message":"Rank must be in range [0, 1000000]."}
$ curl -i "http://127.0.0.1:8000/rank/1000001"
HTTP/1.1 400 Bad Request
date: Sat, 28 Aug 2021 20:48:51 GMT
server: uvicorn
content-length: 49
content-type: application/json

{"message":"Rank must be in range [0, 1000000]."}

If you were to add a different endpoint that uses the same model, the exception handler will automatically take care of this as well, eg:如果您要添加使用相同模型的不同端点,异常处理程序也会自动处理此问题,例如:

@app.get('/other-rank/{rank}')
async def get_other_rank(value: RankInput = Depends()):
    result = get_info_by_rank(value.rank)
    return result
$ curl -i "http://127.0.0.1:8000/other-rank/-1"
HTTP/1.1 400 Bad Request
date: Sat, 28 Aug 2021 20:54:16 GMT
server: uvicorn
content-length: 49
content-type: application/json

{"message":"Rank must be in range [0, 1000000]."}

If this is not what you're looking for, could you explain why exactly you'd like to raise a ValueError ?如果这不是你要找的,你能解释一下为什么你想提出一个ValueError吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用 Pydantic 和 FastAPI 的 PurePath 的 JSON 模式无法声明错误值 - Error Value not declarable with JSON Schema for PurePath with Pydantic and FastAPI FastAPI Hello World 示例:内部服务器错误 - FastAPI Hello World Example: Internal Server Error FASTAPI 删除操作给出内部服务器错误 - FASTAPI Delete Operation giving Internal server error pydantic.error_wrappers.ValidationError: FastAPI - pydantic.error_wrappers.ValidationError: FastAPI 如何在 FastApi 和 Pydantic 中自定义错误模式? - How to customize error schema in FastApi and Pydantic? 是否可以更改 fastAPI 中的 pydantic 错误消息? - Is it possible to change the pydantic error messages in fastAPI? 嵌套 Pydantic Model 使用 FastAPI 返回错误:需要字段(type=value_error.missing) - Nested Pydantic Model return error with FastAPI : field required (type=value_error.missing) 在 FastAPI 中使用 Pydantic 模型进行基于模型的预测时出现错误“value is not a valid dict” - Getting error "value is not a valid dict" when using Pydantic models in FastAPI for model-based predictions pydantic.error_wrappers.ValidationError:使用 FastAPI 的“1”验证错误 - pydantic.error_wrappers.ValidationError: 1 validation error for '' with FastAPI Python Dash 网络应用程序引发内部服务器错误 - Python Dash web-app raises Internal Server Error
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM