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

FastAPI使用APIRouter返回列表报Cannot instantiate typing.Union错误问询

问题诱因

出现该错误的核心原因是FastAPI的APIRouter相较于全局@app装饰器,对返回值类型的推导和校验逻辑更严格,触发报错的具体原因如下:

  • 接口存在两种完全不兼容的返回类型:404场景返回JSONResponse对象,正常场景返回List[AuthModel]列表,APIRouter会自动将返回值推导为Union[JSONResponse, List[AuthModel]],而Pydantic无法直接实例化Union类型,触发报错。
  • AuthModel中使用了自定义的PyObjectId类型,未配置允许Pydantic解析自定义类型的相关参数,类型校验时无法识别该自定义类型。
  • 未显式声明接口的response_model与函数返回值类型,APIRouter自动推导类型时容错性更低,无法正确解析包含自定义类型、可选类型的复合返回结构。
解决方案

按照以下步骤修改即可通过router正常返回数据:

  1. 统一返回类型,替换自定义JSONResponse为HTTPException
    使用FastAPI内置的HTTPException抛出错误响应,无需手动返回JSONResponse,避免接口返回多种不兼容类型。同时注意不要覆盖路径参数domain的变量值,避免逻辑异常。
  2. 为AuthModel添加自定义类型支持配置
    在AuthModel中添加配置项,允许Pydantic解析自定义的PyObjectId类型:
    如果使用Pydantic V1:
class AuthModel(BaseModel):
    id: PyObjectId = Field(default_factory=PyObjectId, alias="_id")
    userid: UUID = Field(default_factory=uuid4)
    firstname: Optional[str]
    lastname: Optional[str]
    domain: UUID = Field(default_factory=uuid4)
    email: EmailStr
    phone: Optional[str]
    plain_secret: Optional[str]
    status: Optional[str]

    class Config:
        arbitrary_types_allowed = True
        allow_population_by_field_name = True

如果使用Pydantic V2:

class AuthModel(BaseModel):
    id: PyObjectId = Field(default_factory=PyObjectId, alias="_id")
    userid: UUID = Field(default_factory=uuid4)
    firstname: Optional[str]
    lastname: Optional[str]
    domain: UUID = Field(default_factory=uuid4)
    email: EmailStr
    phone: Optional[str]
    plain_secret: Optional[str]
    status: Optional[str]

    model_config = {
        "arbitrary_types_allowed": True,
        "populate_by_name": True
    }
  1. 显式声明接口响应模型与返回值类型
    给router路由装饰器添加response_model参数,同时给视图函数添加返回值类型注解,明确告诉FastAPI返回值的结构,关闭自动类型推导:
from typing import List
from fastapi import APIRouter, HTTPException, status

router = APIRouter()

# 替换@app.get为@router.get,显式声明response_model
@router.get("/api/accounts/userslist/{domain}",
          response_description="List all users",
          response_model=List[AuthModel])
async def account_list(
    domain: str,
    credentials: HTTPAuthorizationCredentials = Security(security)) -> List[AuthModel]:
    token = credentials.credentials
    if auth_handler.decode_token(token):
        # 修改变量名避免覆盖路径参数domain
        domain_doc = domains_db.find_one({'domain': domain})
        if not domain_doc:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="There is no user matching the requested domain."
            )
        accounts = users_db.find({'domain': domain_doc['domain_id']})
        return [AuthModel(**account) for account in accounts]

# 务必在FastAPI实例初始化位置注册路由
# app.include_router(router)

内容的提问来源于stack exchange,提问作者ali ardakani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 06:12:03