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

Next.js API路由报错:需按HTTP方法导出命名函数,表单提交405

问题描述

已通过Prisma成功连接PostgreSQL数据库,配置并推送了seed.ts文件,数据已存入数据库。但在app/api/admin/route.js创建API端点时遇到问题,注册表单提交数据到数据库时出现以下错误:

  • 错误:在'/app/api/admin/route.js'中检测到默认导出,请为每个HTTP方法导出命名函数。
  • 错误:'app/api/admin/route.js'中未导出任何HTTP方法,请为每个HTTP方法导出命名函数。
    提交表单后控制台还显示:Failed to load resource: the server responded with a status of 405 (Method Not Allowed)

原API路由代码(app/api/admin/route.js)

import prisma from "../../lib/prisma";

export default async function handler(req, res) {
  const { name, password, email } = req.query;

  try {
      const newUer = await prisma.User.create({
          data: {
              name,
              email,
              password
          },
      });

      res.json({ user: newUer, error: null });
  } catch (error) {
      res.json({ error: error.message, user: null });
  }
}

原表单代码

"use client";

import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import Link from "next/link";

export default function SignUp() {
    const { register, reset, formState: { errors }, handleSubmit } = useForm();
    const router = useRouter();
 
    const onSubmit = async data => {
        console.log(data);

        const user = await fetch("api/admin", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(data),
        })

        /*if (Object.keys(errors).length === 0) {
            router.push("/login"); 
        }
        reset();

        */
    }

    return (
        <div>
            <h1 className="signUp">Sign up</h1>
            <form onSubmit={handleSubmit(onSubmit)} className="signUpForm">
                <input
                    {...register("name", { required: true })} id="first" placeholder="Name"
                    aria-invalid={errors.name ? "true" : "false"}
                />
                {errors.name?.type === 'required' && <p className="errorMessageOne" role="alert">First name is required</p>}

                <input {...register("mail", { required: true })} type="mail" id="email" placeholder="Email Address"
                    aria-invalid={errors.mail ? "true" : "false"} />
                {errors.mail && <p className="errorMessage" role="alert">Please enter a valid email address</p>}

                <input {...register("password", { required: true, minLength: 4 })} id="password" placeholder="Password must contain a minimum of 8 characters"
                    aria-invalid={errors.password ? "true" : "false"} />
                {errors.password && <p className="errorMessage" role="alert">Please enter a valid password</p>}

                <button type="submit" className="signSubmit">Submit</button>
                <p className="alreadyText"> Already have an account? <Link href="/login"><span>Login</span></Link></p>
            </form>
        </div>
    );
}

解决方案

核心问题分析

你使用的是Next.js App Router(app/目录下的API路由),它的规则和旧版Pages Router完全不同:

  1. App Router不允许默认导出handler函数,必须为每个HTTP方法导出对应命名函数(如POST、GET)
  2. POST请求的数据在请求体内,原代码用req.query获取是错误的
  3. 表单邮箱字段注册的是mail,但API中用的是email,字段名不匹配

修复后的API路由代码

import prisma from "../../lib/prisma";

// 导出POST方法处理函数,对应表单的提交请求
export async function POST(request) {
  try {
    // 从请求体解析数据,而非URL参数
    const { name, password, email } = await request.json();
    
    // 注意:确保Prisma Schema中的模型名是User(小写user则改成prisma.user.create)
    const newUser = await prisma.User.create({
      data: {
        name,
        email,
        password
      },
    });

    return Response.json({ user: newUser, error: null });
  } catch (error) {
    // 返回错误状态码,方便前端判断
    return Response.json({ error: error.message, user: null }, { status: 500 });
  }
}

修复后的表单代码

"use client";

import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import Link from "next/link";

export default function SignUp() {
    const { register, reset, formState: { errors }, handleSubmit } = useForm();
    const router = useRouter();
 
    const onSubmit = async data => {
        const response = await fetch("/api/admin", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(data),
        });

        const result = await response.json();
        if (!result.error) {
            router.push("/login"); 
            reset();
        } else {
            console.error("注册失败:", result.error);
        }
    }

    return (
        <div>
            <h1 className="signUp">Sign up</h1>
            <form onSubmit={handleSubmit(onSubmit)} className="signUpForm">
                <input
                    {...register("name", { required: true })} id="first" placeholder="Name"
                    aria-invalid={errors.name ? "true" : "false"}
                />
                {errors.name?.type === 'required' && <p className="errorMessageOne" role="alert">First name is required</p>}

                {/* 统一字段名为email,和API匹配 */}
                <input {...register("email", { 
                    required: true,
                    pattern: /^\S+@\S+\.\S+$/ 
                })} type="email" id="email" placeholder="Email Address"
                    aria-invalid={errors.email ? "true" : "false"} />
                {errors.email?.type === 'required' && <p className="errorMessage" role="alert">Email is required</p>}
                {errors.email?.type === 'pattern' && <p className="errorMessage" role="alert">Please enter a valid email address</p>}

                {/* 修正密码最小长度和提示匹配 */}
                <input {...register("password", { 
                    required: true, 
                    minLength: 8 
                })} id="password" placeholder="Password must contain a minimum of 8 characters"
                    aria-invalid={errors.password ? "true" : "false"} />
                {errors.password?.type === 'required' && <p className="errorMessage" role="alert">Password is required</p>}
                {errors.password?.type === 'minLength' && <p className="errorMessage" role="alert">Password must be at least 8 characters</p>}

                <button type="submit" className="signSubmit">Submit</button>
                <p className="alreadyText"> Already have an account? <Link href="/login"><span>Login</span></Link></p>
            </form>
        </div>
    );
}

额外提示

  • 密码不要明文存储,建议使用bcrypt等加密库对密码加密后再存入数据库
  • 确保Prisma Schema中的模型字段和API、表单字段完全匹配,避免大小写或名称不一致的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 17:43:19