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

Next.js API POST请求报错求助:JSON解析失败

问题:Next.js发送FormData到API时出现JSON解析错误

我是后端、Next.js及TypeScript新手,尝试通过Next.js Server Actions和Route Handlers向API发送携带FormData的POST请求以创建新房源列表,以下是相关代码和遇到的问题:


表单组件代码

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    const form = e.currentTarget as HTMLFormElement;
    const formData = new FormData(form);
    
    const address = formData.get("address");
    const mail = formData.get("mail");
    const region_id = selectedRegion?.id; // 来自状态管理
    const agent_id = selectedAgent?.id;  // 来自状态管理
    const city_id = selectedCity?.id;   // 来自状态管理
    const area = formData.get("area");
    const price = formData.get("price");
    const type = formData.get("type");
    const cover = formData.get("cover"); // 文件输入

    try {
        console.log(address, mail, region_id, agent_id, city_id, area, price, type, cover);

        const newFormData = new FormData();

        newFormData.append("address", address as string);
        newFormData.append("mail", mail as string);
        newFormData.append("region_id", region_id as any);
        newFormData.append("agent_id", agent_id as any);
        newFormData.append("city_id", city_id as any);
        newFormData.append("area", area as string);
        newFormData.append("price", price as string);
        newFormData.append("type", type as string);
        newFormData.append("cover", cover as File); 
        
        await addListingAction(newFormData)
    } catch (error) {
        alert(`Error: ${error}`);
    }
};

Server Action代码

export async function addListingAction(formData: FormData) {
  try {
    await addListing(formData);
  } catch (error) {
    console.log(error);
  } finally {
    revalidatePath("/")
  }
}

api.ts代码

export async function addListing(formData: FormData) {
  return await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/add-listing`, {
    method: 'POST',
    body: formData,
  })
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error('Error:', error));
}

route.ts代码

import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  try {
    const formData = await req.formData();

    const address = formData.get("address");
    const mail = formData.get("mail");
    const region_id = formData.get("region_id");
    const agent_id = formData.get("agent_id");
    const city_id = formData.get("city_id");
    const area = formData.get("area");
    const price = formData.get("price");
    const type = formData.get("type");
    const cover = formData.get("cover") as File | null;

    const form = new FormData();
    if (address) form.append("address", address);
    if (mail) form.append("mail", mail);
    if (region_id) form.append("region_id", region_id);
    if (agent_id) form.append("agent_id", agent_id);
    if (city_id) form.append("city_id", city_id);
    if (area) form.append("area", area);
    if (price) form.append("price", price);
    if (type) form.append("type", type);
    if (cover instanceof File) form.append("cover", cover);

    const REDBERRY_API_TOKEN = process.env.REDBERRY_API_TOKEN;

    if (!REDBERRY_API_TOKEN) {
      throw new Error("Missing API token");
    }

    const response = await fetch('https://api.real-estate-manager.redberryinternship.ge/api/real-estates', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${REDBERRY_API_TOKEN}`,
      },
      body: form,
    });

    if (!response.ok) {
      let errorResponse;
      try {
        errorResponse = await response.json();
      } catch {
        errorResponse = await response.text();
      }
      console.error('HTTP error! Status:', response.status, 'Response:', errorResponse);
      return NextResponse.json(
        { message: 'API request failed', error: errorResponse },
        { status: response.status }
      );
    }

    const listing = await response.json();

    return NextResponse.json({ listing }, { status: 201 });

  } catch (error) {
    const message = error instanceof Error ? error.message : 'An unexpected error occurred';
    console.error('Error creating listing:', message);
    return NextResponse.json(
      { message: 'Failed to create listing', error: message },
      { status: 500 }
    );
  }
}

API文档curl示例

curl -X 'POST' \
  'https://api.real-estate-manager.redberryinternship.ge/api/real-estates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer API_TOKEN' \
  -H 'Content-Type: multipart/form-data' \
  -F 'region_id=1' \
  -F 'price=100000' \
  -F 'zip_code=0101' \
  -F 'area=100.5' \
  -F 'city_id=1' \
  -F 'address=example address' \
  -F 'agent_id=371' \
  -F 'bedrooms=3' \
  -F 'is_rental=0' \
  -F 'image=@review3.png;type=image/png' \
  -F 'description=house near metro station'

API响应示例

{
  "price": "100000",
  "zip_code": "0101",
  "area": "100.5",
  "city_id": "1",
  "address": "example description",
  "agent_id": "371",
  "bedrooms": "3",
  "is_rental": "0",
  "description": "house near metro station",
  "image": "https://api.real-estate-manager.redberryinternship.ge/storage/images/vZ6KRjLiBIoVqwl4GMniE598mAzno8wqzCkZyg3f.png",
  "created_at": "2024-09-15T16:43:21.000000Z",
  "id": 450
}

错误信息

Error creating listing: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
POST /api/add-listing 500 in 931ms
{
message: 'Failed to create listing',
error: 'Unexpected token '<', "<!DOCTYPE "... is not valid JSON'
}


问题排查与解决

  1. 错误根源:Unexpected token '<' 说明请求返回了HTML页面(比如404页面)而非预期的JSON,核心原因是api.ts中process.env.NEXT_PUBLIC_API_URL配置错误,导致请求发向了无效地址。

  2. 具体修复步骤:

    • 修正环境变量:检查.env文件,确保NEXT_PUBLIC_API_URL设为正确值(本地开发填http://localhost:3000),保证请求能命中你的/api/add-listing路由。
    • 简化请求链路:Server Action可以直接调用第三方API,无需经过api.ts和Route Handler,减少中间环节。修改addListingAction:
      export async function addListingAction(formData: FormData) {
        try {
          const REDBERRY_API_TOKEN = process.env.REDBERRY_API_TOKEN;
          if (!REDBERRY_API_TOKEN) throw new Error("Missing API token");
      
          const response = await fetch('https://api.real-estate-manager.redberryinternship.ge/api/real-estates', {
            method: 'POST',
            headers: {
              'Authorization': `Bearer ${REDBERRY_API_TOKEN}`,
            },
            body: formData,
          });
      
          if (!response.ok) {
            let errorResponse;
            try {
              errorResponse = await response.json();
            } catch {
              errorResponse = await response.text();
            }
            throw new Error(`API error: ${response.status} - ${JSON.stringify(errorResponse)}`);
          }
      
          await response.json();
        } catch (error) {
          console.error(error);
          throw error; // 抛出错误让前端捕获
        } finally {
          revalidatePath("/")
        }
      }
      
    • 匹配API字段要求:对比curl示例,你的请求缺少zip_code、bedrooms、is_rental、description必填字段,且文件字段名应为image而非cover,修改表单组件的FormData处理:
      // 修正文件字段名
      newFormData.append("image", cover as File);
      // 补充必填字段(需确保表单中有对应输入项)
      newFormData.append("zip_code", formData.get("zip_code") as string);
      newFormData.append("bedrooms", formData.get("bedrooms") as string);
      newFormData.append("is_rental", formData.get("is_rental") as string);
      newFormData.append("description", formData.get("description") as string);
      
    • 优化类型断言:避免as any,确保ID类字段转为字符串(API示例中均为字符串值):
      newFormData.append("region_id", region_id?.toString() || "");
      newFormData.append("agent_id", agent_id?.toString() || "");
      newFormData.append("city_id", city_id?.toString() || "");
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 07:22:34