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

Next.js 14 POST请求FastAPI返回422错误,请求体匹配Pydantic模型

解决Next.js 14 POST到FastAPI出现422 Unprocessable Entity错误的问题

你遇到的422错误提示email字段缺失,但实际已传入请求体,甚至移除请求体后错误依旧,大概率是请求头缺失、Next.js代理路由配置错误或模型定义问题,以下是具体排查和解决步骤:

1. 为Next.js请求添加Content-Type头

FastAPI默认仅解析Content-Type: application/json的请求体,Next.js的fetch若未显式设置该头,可能导致FastAPI无法识别请求体格式,进而判定字段缺失。

修改你的Next.js fetch代码:

await fetch("/api/contacts", {
  method: "POST",
  headers: {
    "Content-Type": "application/json", // 必须添加这个头
  },
  body: JSON.stringify({ email: "a@b.com" }),
});

2. 检查Next.js API路由的代理逻辑

如果你的/api/contacts是Next.js自身的API路由(作为中间层转发请求到FastAPI),需要确保路由正确读取并转发请求体和头:

创建或修改app/api/contacts/route.js:

export async function POST(request) {
  // 先读取Next.js收到的请求体
  const requestBody = await request.json();
  
  // 转发到FastAPI实际端点
  const fastApiResponse = await fetch("http://your-fastapi-domain/contacts", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(requestBody),
  });

  // 返回FastAPI的响应给前端
  const data = await fastApiResponse.json();
  return Response.json(data, { status: fastApiResponse.status });
}

如果之前的代理路由未读取请求体就直接转发,或未设置Content-Type头,会导致FastAPI收不到有效数据。

3. 验证FastAPI的Contact模型定义

确认Contact模型的email字段名称和类型与请求体完全匹配,避免大小写或类型错误:

from pydantic import BaseModel, EmailStr

class Contact(BaseModel):
    email: EmailStr  # 确保字段名是小写的email,且类型正确

4. 排查请求是否真正到达FastAPI

在FastAPI端点添加日志,查看实际收到的请求体:

from fastapi import Request, FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

class Contact(BaseModel):
    email: EmailStr

@app.post("/contacts")
async def create_contact(request: Request, contact: Contact):
    # 打印实际收到的请求体
    print("Received request body:", await request.json())
    return contact

如果日志显示请求体为空,说明问题出在Next.js的请求发送或代理环节,回到前两步排查。


内容的提问来源于stack exchange,提问作者Debanshu Paul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 14:22:36