You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用FastAPI接口出现HTTP 422 Unprocessable Entity错误原因排查

FastAPI接口422错误排查与解决

问题背景

用FastAPI开发/patient/add_patient接口后,调用时返回HTTP 422 Unprocessable Entity错误。已知Users表的password字段允许为空,代码中已将其赋值为None,同时需要理解错误信息中loc is body的含义。

相关代码

patient路由文件

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status
from starlette import status

# Local Imports
from database import SessionLocal
# from .auth import CreateUserRequest, db_dependency, user_dependency
from models import Patients, Users


router = APIRouter(
    prefix='/patient',
    tags=['patient']
)

async def get_db():
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    db = SessionLocal()
    try:
        yield db
    finally:
        await db.close()


# Annotation for our database for dependency injection
db_dependency = Annotated[AsyncSession, Depends(get_db)]

async def get_curent_user(token: Annotated[str, Depends(oauth2_bearer)]):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        email: str = payload.get('sub')
        user_id: int = payload.get('id')
        role_id: int = payload.get('role_id')
        if email is None or user_id is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Could not validate user."
            )
        return {'username': email, 'id': user_id, 'role_id': role_id}
    except JWTError:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
        detail='Could not validate user.')

user_dependency = Annotated[dict, Depends(get_curent_user)]


class CreateUserRequest(BaseModel):
    username: str # Email is username
    password: str
    phone: str
    dob: date
    role_id: int


class CreatePatinetReuqest(BaseModel):
    name: str
    registration_date: date
    user_id: int


@router.post("/add_patient/")
async def add_patient(db: db_dependency, create_user_request: CreateUserRequest):
    # Create a new user (patient)
    create_user_model = Users(
        email=create_user_request.username,
        password=None,
        phone=create_user_request.phone,
        date_of_birth=create_user_request.dob,
        role_id=0,
    )
    try:
        db.add(create_user_model)
        await db.commit()
    except IntegrityError as er:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"User {create_user_request.username} already exists. Choose a different username."
        )
    await db.refresh(create_user_model)

    # Create a corresponding patient entry
    add_patient_model = Patients(
        registration_date=date.today(),
        user_id=create_user_model.id
    )

    try:
        db.add(add_patient_model)
        await db.commit()
    except IntegrityError as er:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Patient with {add_patient_model.patient_id} already exists."
        )
    await db.refresh(add_patient_model)

    return {
        "message": "Patient added successfully",
        "patient_id": add_patient_model.patient_id
    }

main.py文件

from fastapi import FastAPI

from routers import patient


app = FastAPI()
app.include_router(patient.router)

错误截图

错误截图


错误原因分析

1. Pydantic模型校验失败

接口函数add_patient接收的CreateUserRequest模型中,password被定义为必填的str类型,但调用接口时可能未传入该字段,或者传入值不符合要求。尽管代码中最终将Users的password设为None,但FastAPI会先通过Pydantic校验请求体,只要请求缺少必填字段就会触发422错误。

2. 代码中存在其他潜在问题

  • CreatePatinetReuqest存在拼写错误(应为CreatePatientRequest),不过该模型未被当前接口使用,暂不影响。
  • get_db函数中使用的engine未导入,会导致运行时错误。
  • Annotated、Depends、oauth2_bearer、jwt、SECRET_KEY、ALGORITHM、date等依赖未导入,会引发NameError。

loc is body的含义

错误信息中的loc(location)字段用于定位错误发生的位置,loc: ['body']表示错误出现在请求体(Request Body)中,即发送的JSON请求数据不符合接口要求的Pydantic模型规则,比如缺少必填字段、字段类型不匹配、格式错误等。


修正方案

1. 调整Pydantic模型,允许password可选

既然Users表的password字段允许为空,将CreateUserRequest中的password字段改为可选类型,并设置默认值为None:

from pydantic import BaseModel, Optional
from datetime import date

class CreateUserRequest(BaseModel):
    username: str  # Email is username
    password: Optional[str] = None  # 设为可选字段,默认值None
    phone: str
    dob: date
    role_id: int

2. 补全缺失的导入

在patient路由文件顶部添加缺失的依赖导入:

from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel, Optional
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
from starlette import status
from typing import Annotated
from jose import JWTError, jwt
from datetime import date

# 补充engine和Base的定义(若database.py未包含)
engine = create_async_engine("你的异步数据库连接URL")
Base = declarative_base()

3. 优化接口逻辑(可选)

如果该接口专门用于添加患者,可直接在代码中固定role_id为0,去掉模型中的role_id字段,简化请求参数:

class CreateUserRequest(BaseModel):
    username: str  # Email is username
    password: Optional[str] = None
    phone: str
    dob: date

# 创建用户时直接使用固定的role_id
create_user_model = Users(
    email=create_user_request.username,
    password=create_user_request.password,
    phone=create_user_request.phone,
    date_of_birth=create_user_request.dob,
    role_id=0,
)

内容的提问来源于stack exchange,提问作者Ciasto piekarz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 19:05:57