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

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。

修复步骤:

  1. 调整API路由文件:App Router的API路由需命名为route.ts,将src/app/api/products/page.ts重命名为src/app/api/products/route.ts。
  2. 修改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);
}
  1. 修正前端请求路径:使用绝对路径避免相对路径错误:
await axios.post('/api/products', {title, description, price});
  1. 优化前端输入:将价格输入框改为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 08:27:09