使用 pydatinc 对 FastAPI 的查询参数进行自定义验证会导致内部服务器错误

ooo*_*ooo 6 python exception valueerror pydantic fastapi

我的GET端点收到一个需要满足以下条件的查询参数:

  1. 介于int0 和 10 之间
  2. 是偶数

1.直接使用Query(gt=0, lt=10). 但是,我不清楚如何扩展Query以进行额外的自定义验证,例如2.. 该文档最终导致了 pydantic。但是,当第二次验证失败时,我的应用程序遇到内部服务器错误2.

下面是一个最小范围的示例

from fastapi import FastAPI, Depends, Query
from pydantic import BaseModel, ValidationError, validator

app = FastAPI()

class CommonParams(BaseModel):
    n: int = Query(default=..., gt=0, lt=10)

    @validator('n')
    def validate(cls, v):
        if v%2 != 0:
            raise ValueError("Number is not even :( ")
        return v


@app.get("/")
async def root(common: CommonParams = Depends()):
    return {"n": common.n}
Run Code Online (Sandbox Code Playgroud)

以下是按预期工作的请求和失败的请求:

# requsts that work as expected
localhost:8000?n=-4
localhost:8000?n=-3
localhost:8000?n=2
localhost:8000?n=8
localhost:8000?n=99

# request that break server
localhost:8000?n=1
localhost:8000?n=3
localhost:8000?n=5
Run Code Online (Sandbox Code Playgroud)

Chr*_*ris 4

选项1

直接加注HTTPException而不是,如本答案ValueError的选项 1 所示。例子:

from fastapi import FastAPI, Depends, Query, HTTPException
from pydantic import BaseModel, validator

app = FastAPI()

class CommonParams(BaseModel):
    n: int = Query(default=..., gt=0, lt=10)

    @validator('n')
    def prevent_odd_numbers(cls, v):
        if v % 2 != 0:
            raise HTTPException(status_code=422, detail='Input number is not even')
        return v


@app.get('/')
async def root(common: CommonParams = Depends()):
    return {'n': common.n}
Run Code Online (Sandbox Code Playgroud)

服务器响应(当输入的数字不是偶数时,例如n = 1):

# 422 Error: Unprocessable Entity

{
  "detail": "Input number is not even"
}
Run Code Online (Sandbox Code Playgroud)

选项2

使用自定义异常处理程序来处理ValueError异常,类似于此答案此答案。例子:

from fastapi import FastAPI, Request, Depends, Query, status
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel, validator

app = FastAPI()

@app.exception_handler(ValueError)
async def validation_exception_handler(request: Request, exc: ValueError):
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content=jsonable_encoder({"detail": exc.errors(),  # optionally, include the pydantic errors
                 "custom msg": {"Your error message"}}),   # optionally, return a custom message
    )
    
class CommonParams(BaseModel):
    n: int = Query(default=..., gt=0, lt=10)

    @validator('n')
    def prevent_odd_numbers(cls, v):
        if v % 2 != 0:
            raise ValueError('Input number is not even')
        return v


@app.get('/')
async def root(common: CommonParams = Depends()):
    return {'n': common.n}
Run Code Online (Sandbox Code Playgroud)

服务器响应(当输入的数字不是偶数时,例如n = 1):

# 422 Error: Unprocessable Entity

{
  "detail": [
    {
      "loc": [
        "n"
      ],
      "msg": "Input number is not even",
      "type": "value_error"
    }
  ],
  "custom msg": [
    "Your error message"
  ]
}
Run Code Online (Sandbox Code Playgroud)

更新

请注意,在 Pydantic V2 中,@validator已被弃用并被@field_validator. 请查看此答案以获取更多详细信息和示例。