Prisma.user.update()报错:nome参数不可为null的解决求助
解决Prisma更新用户名称时
nome参数不可为null的问题 问题描述
使用Prisma更新数据库中的用户名称(nome)时,系统提示nome参数不可为null,但表单中已填写该字段,需排查解决此问题。
报错信息
Error: Invalid `prisma.user.update()` invocation: { where: { id: "kp_fa1706a59b5348b384c9e62cb0bf59df" }, data: { + nome: String } } Argument `nome` must not be null.
原始代码
Server Action函数
"use server"; import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server"; import { redirect } from "next/navigation"; import { prisma } from "./lib/db"; import { Prisma } from "@prisma/client"; export async function updateName() { const { getUser } = getKindeServerSession(); const user = await getUser(); const formData = new FormData(); if (!user) { return redirect("/api/auth/login"); } const nome = formData.get("nome"); try { await prisma.user.update({ where: { id: user.id, }, data: { nome: nome, }, }); return { message: "Succesfully Updated name", status: "green", }; } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { if (e.code === "P2002") { return { message: "This username is alredy used", status: "error", }; } } throw e; } }
Prisma Schema定义
model User { id String @id nome String cognome String email String imageUrl String? Paziente Paziente[] }
补充代码(表单与提交按钮)
// 表单组件 "use client"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import Link from "next/link"; import { updateName } from "@/app/actions"; import { SubmitButton } from "../utility/SubmitButtons"; import { useFormState } from "react-dom"; import { useEffect } from "react"; import { useToast } from "@/components/ui/use-toast"; const initialState = { message: "", status: "", }; export function SettingsForm({ nome }) { const [state, formAction] = useFormState(updateName, initialState); const { toast } = useToast(); useEffect(() => { if (state?.status === "green") { toast({ title: "Succesfull", description: state.message, }); } else if (state?.status === "error") { toast({ title: "Error", description: state.message, variant: "destructive", }); } }, [state, toast]); return ( <form action={formAction}> <h1 className="text-3xl font-extrabold tracking-tight">Settings</h1> <Separator className="my-4" /> <Label className="text-lg">Name</Label> <p className="text-muted-foreground"> In this Settings page you can change your name! </p> <Input defaultValue={nome} name="nome" required className="mt-2" min={2} maxLength={21} /> {state?.status === "error" && ( <p className="text-destructive mt-1">{state.message}</p> )} <div className="w-full flex mt-5 gap-x-5 justify-end"> <Button variant="secondary" asChild type="button"> <Link href="/">Cancel</Link> </Button> <SubmitButton text="Change Username" /> </div> </form> ); } // 提交按钮组件 "use client"; import { Button } from "@/components/ui/button"; import { ArrowDown, ArrowUp, Loader2 } from "lucide-react"; import { useFormStatus } from "react-dom"; export function SubmitButton({ text }) { const { pending } = useFormStatus(); return ( <> {pending ? ( <Button disabled> <Loader2 className="mr-2 w-4 h-4 animate-spin" /> Please wait </Button> ) : ( <Button type="submit">{text}</Button> )} </> ); } export function SaveButton() { const { pending } = useFormStatus(); return ( <> {pending ? ( <Button className="mt-2 w-full" disabled size="sm"> <Loader2 className="mr-2 h-3 w-3 animate-spin" /> Please wait </Button> ) : ( <Button size="sm" className="mt-2 w-full" type="submit"> Save </Button> )} </> ); } export function UpVote() { const { pending } = useFormStatus(); return ( <> {pending ? ( <Button variant="outline" size="icon" disabled> <Loader2 className="h-4 w-4 animate-spin" /> </Button> ) : ( <Button variant="outline" size="sm" type="submit"> <ArrowUp className="h-4 w-4" /> </Button> )} </> ); } export function DownVote() { const { pending } = useFormStatus(); return ( <> {pending ? ( <Button variant="outline" size="icon" disabled> <Loader2 className="h-4 w-4 animate-spin" /> </Button> ) : ( <Button variant="outline" size="sm" type="submit"> <ArrowDown className="h-4 w-4" /> </Button> )} </> ); }
问题根源
Server Action函数中手动创建了空的FormData对象,导致formData.get("nome")返回null。Next.js的Server Action在表单提交时,会自动将用户填写的表单数据以FormData的形式作为参数传递给action函数,无需手动实例化FormData。
解决方案
修改Server Action函数,接收自动传递的FormData参数,并添加必要的参数校验:
修改后的Server Action
"use server"; import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server"; import { redirect } from "next/navigation"; import { prisma } from "./lib/db"; export async function updateName(formData) { const { getUser } = getKindeServerSession(); const user = await getUser(); if (!user) { return redirect("/api/auth/login"); } // 从表单数据中获取nome字段 const nome = formData.get("nome"); // 添加参数校验:确保nome是有效字符串且不为空 if (!nome || typeof nome !== "string" || nome.trim().length === 0) { return { message: "名称不能为空", status: "error", }; } try { await prisma.user.update({ where: { id: user.id, }, data: { nome: nome.trim(), // 去除首尾空格后存入数据库 }, }); return { message: "名称更新成功", status: "green", }; } catch (e) { // 处理Prisma已知错误 if (e instanceof prisma.Prisma.PrismaClientKnownRequestError) { if (e.code === "P2002") { return { message: "该用户名已被使用", status: "error", }; } } // 处理其他未知错误 return { message: "更新失败,请稍后重试", status: "error", }; } }
额外优化建议
- 前端表单中,
Input组件的min属性改为minLength(min针对数字输入,字符串输入需用minLength):
<Input defaultValue={nome} name="nome" required className="mt-2" minLength={2} maxLength={21} />
- 可在前端添加实时校验,提前拦截无效输入,提升用户体验。
内容的提问来源于stack exchange,提问作者Federico Fan
相关产品推荐
相关产品推荐

