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'
}
问题排查与解决
错误根源:
Unexpected token '<'说明请求返回了HTML页面(比如404页面)而非预期的JSON,核心原因是api.ts中process.env.NEXT_PUBLIC_API_URL配置错误,导致请求发向了无效地址。具体修复步骤:
- 修正环境变量:检查
.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
相关产品推荐
相关产品推荐

