调用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
相关产品推荐
相关产品推荐

