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

Next.js 14 + Appwrite后端报错:TypeError: e._formData.get is not a function

解决Next.js 14 + Appwrite表单提交报错:TypeError: e._formData.get is not a function

使用Next.js 14搭配Appwrite后端时,表单首次提交后无论成功与否都会触发TypeError: e._formData.get is not a function错误,刷新页面后后续提交仍持续报错。

相关代码

SignUpPage.tsx

import SignupForm from "./components/signupForm";
import axios from "axios";

const onSubmit = async (name: string, email: string, password: string) => {
  "use server";
  console.log("SERVER name", name);
  console.log("SERVER email", email);
  console.log("SERVER password", password);

  const response = await fetch("http://localhost:3000/api/auth/signup", {
    method: "POST",
    headers: {
      "Content-Type": "application/JSON",
    },
    body: JSON.stringify({
      name,
      email,
      password,
    }),
  });
  console.log("response", JSON.parse(JSON.stringify(response)));

  if (response.status === 200) {
    return JSON.parse(JSON.stringify(response));
  }

  return {
    status: response.status,
  };
};

const SignUpPage = () => {
  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-100 px-6">
      <div className="w-full max-w-md">
        <h1 className="mb-6 text-center text-3xl font-bold text-gray-900">
          Registro
        </h1>
        <SignupForm onSubmit={onSubmit} />
      </div>
    </div>
  );
};

export default SignUpPage;

signupForm.tsx

"use client";
import { FormEvent } from "react";
import { useState } from "react";

const SignupForm = ({
  onSubmit,
}: {
  onSubmit: (name: string, email: string, password: string) => void;
}) => {
  const [errorMsg, setErrorMsg] = useState('');
  const [isLoading, setIsLoading] = useState(false);


  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const formData = new FormData(event.currentTarget);
    const email = formData.get("email") as string;
    const name = formData.get("name") as string;
    const password = formData.get("password") as string;
    setIsLoading(true);

    console.log("name", name);
    console.log("email", email);
    console.log("pass", password);
    try {
      const response = await onSubmit(name, email, password);
      console.log("response", response);
    } catch (error:any) {
      console.log('ERROR: ', error?.message);
      setErrorMsg(error?.message || 'ERROR');
    }finally{
      setIsLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="bg-white shadow-lg rounded-lg p-8">
      <div className="mb-4">
        <label
          className="block text-gray-700 text-sm font-semibold mb-2"
          htmlFor="name"
        >
          Nombre
        </label>
        <input
          type="text"
          name="name"
          id="name"
          placeholder="Tu nombre completo"
          required
          className="shadow-sm bg-gray-50 border border-gray-300 rounded-md py-2 px-3 block w-full outline-none focus:ring-indigo-500 focus:border-indigo-500 text-gray-700"
        />
      </div>
      <div className="mb-4">
        <label
          className="block text-gray-700 text-sm font-semibold mb-2"
          htmlFor="email"
        >
          Email
        </label>
        <input
          type="email"
          name="email"
          id="email"
          placeholder="tuemail@example.com"
          required
          className="shadow-sm bg-gray-50 border border-gray-300 rounded-md py-2 px-3 block w-full outline-none focus:ring-indigo-500 focus:border-indigo-500 text-gray-700"
        />
      </div>
      <div className="mb-6">
        <label
          className="block text-gray-700 text-sm font-semibold mb-2"
          htmlFor="password"
        >
          Contraseña
        </label>
        <input
          type="password"
          name="password"
          id="password"
          placeholder="••••••••"
          required
          className="shadow-sm bg-gray-50 border border-gray-300 rounded-md py-2 px-3 block w-full outline-none focus:ring-indigo-500 focus:border-indigo-500 text-gray-700"
        />
      </div>
      {errorMsg && <p className="error">{errorMsg}</p>}
      <div className="mb-4">
        <button
        disabled={isLoading}
          type="submit"
          className="w-full py-2 px-4 bg-indigo-600 hover:bg-indigo-700 rounded-md shadow-sm text-white text-sm font-medium focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
        >
          {isLoading ? 'Cargando...' : 'Registrar'}
        </button>
      </div>
    </form>
  );
};

export default SignupForm;

route.tsx

import { Account, Client, ID } from "appwrite";
import { revalidatePath } from "next/cache";
import { NextRequest, NextResponse } from "next/server";

type Payload = {
  email: string;
  name: string;
  password: string;
};

export async function GET(request: NextRequest) {
  return NextResponse.json({ messsage: "Hello World" });
}

export async function POST(request: NextRequest) {
  const body: Payload = await request.json();
  const { email, name, password } = body;

  try {
    const client = new Client()
      .setEndpoint(`${process.env.APPWRITE_ENDPOINT}`) // Your API Endpoint
      .setProject(`${process.env.APPWRITE_PROJECT_ID}`);

    const account = new Account(client);
    console.log("account", JSON.stringify(account));
    const result = await account.create(ID.unique(), email, password, name);

    const path = request.nextUrl.searchParams.get('path') || '/';
    revalidatePath(path);

    return NextResponse.json({
      status: 201,
      message: "Account Created",
      data: JSON.stringify(result),
      error: null,
    });
  } catch (error: any) {
    console.error("Error:", error);
    if (error.code === 409) {
      return NextResponse.json({
        status: 409,
        message: "A user with the same id, email, or phone already exists.",
        error: error.response,
      });
    }

    return NextResponse.json({
      status: error.code || 500,
      message: "An error occurred",
      error: error.response,
    });
  }
}

报错截图

报错截图


问题根源

  1. 冗余的Server Action + API路由调用:你在Server Action中又调用了自身的API路由,不仅多此一举,还因为Server Action返回了序列化的Response对象,导致Next.js处理表单后续提交时出现FormData相关异常。
  2. 返回值处理不规范:直接序列化Response对象返回给客户端,不符合Server Action的返回规范,引发表单状态错乱。

修复方案

步骤1:简化Server Action,直接处理Appwrite逻辑

修改SignUpPage.tsx,去掉fetch调用,直接在Server Action中完成Appwrite用户创建:

import SignupForm from "./components/signupForm";
import { Account, Client, ID } from "appwrite";

const onSubmit = async (name: string, email: string, password: string) => {
  "use server";
  console.log("SERVER name", name);
  console.log("SERVER email", email);
  console.log("SERVER password", password);

  try {
    const client = new Client()
      .setEndpoint(`${process.env.APPWRITE_ENDPOINT}`)
      .setProject(`${process.env.APPWRITE_PROJECT_ID}`);

    const account = new Account(client);
    const result = await account.create(ID.unique(), email, password, name);

    return {
      status: 201,
      message: "注册成功",
      data: result,
      error: null,
    };
  } catch (error: any) {
    console.error("Error:", error);
    if (error.code === 409) {
      return {
        status: 409,
        message: "该邮箱已被注册",
        error: error.message,
      };
    }

    return {
      status: error.code || 500,
      message: "注册失败",
      error: error.message,
    };
  }
};

const SignUpPage = () => {
  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-100 px-6">
      <div className="w-full max-w-md">
        <h1 className="mb-6 text-center text-3xl font-bold text-gray-900">
          注册
        </h1>
        <SignupForm onSubmit={onSubmit} />
      </div>
    </div>
  );
};

export default SignUpPage;

步骤2:修正客户端表单的类型定义与错误处理

修改signupForm.tsx,确保onSubmit类型为异步函数,并完善错误提示:

"use client";
import { FormEvent } from "react";
import { useState } from "react";

const SignupForm = ({
  onSubmit,
}: {
  onSubmit: (name: string, email: string, password: string) => Promise<{
    status: number;
    message: string;
    data?: any;
    error?: string;
  }>;
}) => {
  const [errorMsg, setErrorMsg] = useState('');
  const [isLoading, setIsLoading] = useState(false);


  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const formData = new FormData(event.currentTarget);
    const email = formData.get("email") as string;
    const name = formData.get("name") as string;
    const password = formData.get("password") as string;
    setIsLoading(true);
    setErrorMsg('');

    try {
      const response = await onSubmit(name, email, password);
      console.log("response", response);
      if (response.status === 201) {
        // 注册成功后可跳转至登录页
        window.location.href = '/login';
      } else {
        setErrorMsg(response.message);
      }
    } catch (error:any) {
      console.log('ERROR: ', error?.message);
      setErrorMsg(error?.message || '注册出错');
    }finally{
      setIsLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="bg-white shadow-lg rounded-lg p-8">
      <div className="mb-4">
        <label
          className="block text-gray-700 text-sm font-semibold mb-2"
          htmlFor="name"
        >
          姓名
        </label>
        <input
          type="text"
          name="name"
          id="name"
          placeholder="请输入姓名"
          required
          className="shadow-sm bg-gray-50 border border-gray-300 rounded-md py-2 px-3 block w-full outline-none focus:ring-indigo-500 focus:border-indigo-500 text-gray-700"
        />
      </div>
      <div className="mb-4">
        <label
          className="block text-gray-700 text-sm font-semibold mb-2"
          htmlFor="email"
        >
          邮箱
        </label>
        <input
          type="email"
          name="email"
          id="email"
          placeholder="your@email.com"
          required
          className="shadow-sm bg-gray-50 border border-gray-300 rounded-md py-2 px-3 block w-full outline-none focus:ring-indigo-500 focus:border-indigo-500 text-gray-700"
        />
      </div>
      <div className="mb-6">
        <label
          className="block text-gray-700 text-sm font-semibold mb-2"
          htmlFor="password"
        >
          密码
        </label>
        <input
          type="password"
          name="password"
          id="password"
          placeholder="••••••••"
          required
          className="shadow-sm bg-gray-50 border border-gray-300 rounded-md py-2 px-3 block w-full outline-none focus:ring-indigo-500 focus:border-indigo-500 text-gray-700"
        />
      </div>
      {errorMsg && <p className="error text-red-500 text-sm mb-4">{errorMsg}</p>}
      <div className="mb-4">
        <button
        disabled={isLoading}
          type="submit"
          className="w-full py-2 px-4 bg-indigo-600 hover:bg-indigo-700 rounded-md shadow-sm text-white text-sm font-medium focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
        >
          {isLoading ? '加载中...' : '注册'}
        </button>
      </div>
    </form>
  );
};

export default SignupForm;

步骤3:删除冗余API路由(可选)

既然已经在Server Action中处理了Appwrite逻辑,app/api/auth/signup/route.tsx可以直接删除,避免代码冗余。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:17:02