Next.js13新路由下TypeError读取json未定义问题求助
问题描述
使用TypeScript搭配Node.js和Next.js 13新App Router,通过Mongoose向MongoDB推送数据时,持续报错:TypeError: Cannot read properties of undefined (reading 'json')。浏览器Network面板能看到请求payload,但数据无法存入数据库。需求是创建新产品时将数据存入数据库。
product/new/page.tsx
"use client" import Plan from "@/app/components/Plan"; import axios from "axios"; import { useState } from "react"; export default function NewProduct() { const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const [price, setPrice] = useState(''); async function createProduct(ev:any) { ev.preventDefault(); await axios.post('../../api/products', {title, description, price}); } return ( <Plan> <div className="max-w-md mx-auto mt-14"> <h2 className="text-2xl font-bold mb-4">添加新产品</h2> <form onSubmit={ createProduct }> <div className="mb-4"> <label htmlFor="productName" className="block text-gray-700 text-sm font-bold mb-2"> 产品名称: </label> <input type="text" className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="输入产品名称" value={title} onChange={ev => setTitle(ev.target.value)} /> </div> <div className="mb-4"> <label htmlFor="description" className="block text-gray-700 text-sm font-bold mb-2"> 描述: </label> <textarea className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="输入产品描述" value={description} onChange={ev => setDescription(ev.target.value)} /> </div> <div className="mb-4"> <label htmlFor="price" className="block text-gray-700 text-sm font-bold mb-2"> 价格: </label> <input type="text" className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="输入产品价格" value={price} onChange={ev => setPrice(ev.target.value)} /> </div> <div className="flex items-center justify-end"> <button type="submit" className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"> 添加 </button> </div> </form> </div> </Plan> ) }
src/app/api/products/page.ts
import { NextApiRequest, NextApiResponse } from "next"; import { NextResponse } from "next/server"; import { mongooseConnect } from "../../../../lib/mongoose"; import { Product } from "../../../../models/Product"; export default async function handle(req: NextApiRequest, res: NextApiResponse) { const { method } = req; await mongooseConnect(); console.log({res}) console.log({req}) const { title, description, price } = req.body || {}; const productDoc = await Product.create({ title, description, price, }); const data = res.json(productDoc); return NextResponse.json(data); }
调试输出
{ res: undefined } { req: { params: {}, searchParams: {} } }
models/Product.ts
import mongoose, { Schema } from "mongoose"; const ProductSchema = new Schema({ title: String, description: String, price: Number, }); export const Product = mongoose.models.Product || mongoose.model('Product', ProductSchema);
lib/mongoose.ts
import mongoose from "mongoose"; export function mongooseConnect() { const uri = process.env.MONGODB_URI; if (mongoose.connection.readyState === 1) { return Promise.resolve(mongoose.connection); } else { return mongoose.connect(uri!); } }
lib/mongodb.ts
// 该方案参考Next.js官方MongoDB示例 import { MongoClient } from "mongodb"; if (!process.env.MONGODB_URI) { throw new Error('无效/缺失环境变量: "MONGODB_URI"'); } const uri = process.env.MONGODB_URI; const options = {}; let client; let clientPromise: Promise<MongoClient>; if (process.env.NODE_ENV === "development") { // 开发环境下使用全局变量,避免HMR导致的模块重载丢失连接 if (!global._mongoClientPromise) { client = new MongoClient(uri, options); global._mongoClientPromise = client.connect(); } clientPromise = global._mongoClientPromise; } else { // 生产环境不使用全局变量 client = new MongoClient(uri, options); clientPromise = client.connect(); } // 导出模块作用域的MongoClient Promise,方便在多个函数间共享 export default clientPromise;
tsconfig.json
{ "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./src/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "models/Product.js", "src/app/api/products/page.js", "src/app/api/products.js"], "exclude": ["node_modules"] }
解决方案
核心问题是Next.js 13 App Router的API路由写法错误,误用了旧版Pages Router的逻辑,导致res参数未被正确传入,出现undefined。
修复步骤:
- 调整API路由文件:App Router的API路由需命名为
route.ts,将src/app/api/products/page.ts重命名为src/app/api/products/route.ts。 - 修改API路由函数:App Router使用导出的HTTP方法函数(如
POST)而非默认的handle函数,且直接接收Request对象:
import { NextResponse } from "next/server"; import { mongooseConnect } from "../../../../lib/mongoose"; import { Product } from "../../../../models/Product"; export async function POST(request: Request) { await mongooseConnect(); // 解析请求体 const { title, description, price } = await request.json(); // 转换价格为数字(前端传的是字符串) const productDoc = await Product.create({ title, description, price: parseFloat(price), }); return NextResponse.json(productDoc); }
- 修正前端请求路径:使用绝对路径避免相对路径错误:
await axios.post('/api/products', {title, description, price});
- 优化前端输入:将价格输入框改为
type="number",减少类型转换问题:
<input type="number" className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" placeholder="输入产品价格" value={price} onChange={ev => setPrice(ev.target.value)} />
额外检查
确保.env.local文件中已正确配置MONGODB_URI环境变量。
内容的提问来源于stack exchange,提问作者Lil Gnome
相关产品推荐
相关产品推荐

