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完全不同:
- App Router不允许默认导出
handler函数,必须为每个HTTP方法导出对应命名函数(如POST、GET) - POST请求的数据在请求体内,原代码用
req.query获取是错误的 - 表单邮箱字段注册的是
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
相关产品推荐
相关产品推荐

