结合Zod与Prisma做用户输入验证遇问题:验证失效、错误未捕获
解决Zod验证失效与Prisma错误未捕获问题
一、Zod验证未生效的解决
你只定义了Zod验证规则,但从来没在控制器里调用验证逻辑,自然不会生效。
修复步骤:
- 在
user.model.ts中导出定义好的userSchema(原来只导出了类型):
import z from "zod"; const userSchema = z.object({ username: z.string({ required_error: "Username field is required", }), email: z .string({ required_error: "Email field is required", invalid_type_error: "This field must be in email format", }) .email(), password: z .string({ required_error: "Password field is required", }) .min(6, "Password must be at least 6 characters"), }); export type UserModel = z.infer<typeof userSchema>; export { userSchema }; // 新增导出
- 在
authController.ts中导入userSchema,并在处理请求前验证输入:
import { userSchema } from './user.model'; import { z } from 'zod'; import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; import bcrypt from 'bcrypt'; import { createUser } from './user.service'; import { Request, Response } from 'express'; export const signUpLogic = async (req: Request, res: Response) => { try { // 第一步:用Zod验证请求体,验证不通过会直接抛出ZodError const validatedData = userSchema.parse(req.body); const { username, email, password } = validatedData; // 加密密码 const hashedPassword = await bcrypt.hash(password, 10); // 后续逻辑... } catch (err) { // 后续错误处理... } };
二、Prisma错误未捕获的解决
核心问题有两个:调用异步的createUser时没加await,导致错误无法进入当前try/catch;另外错误处理分支太窄,没覆盖所有情况。
修复步骤:
- 给
createUser调用加await,确保异步错误能被捕获:
// 原代码:createUser(username, email, String(hashedPassword)); // 修改为: const newUser = await createUser(username, email, hashedPassword);
- 完善
catch块的错误处理,覆盖Zod验证错误、Prisma错误和通用错误:
export const signUpLogic = async (req: Request, res: Response) => { try { const validatedData = userSchema.parse(req.body); const { username, email, password } = validatedData; const hashedPassword = await bcrypt.hash(password, 10); const newUser = await createUser(username, email, hashedPassword); // 成功响应:返回用户信息(不要返回密码) res.status(201).json({ message: "用户创建成功", user: { id: newUser.id, username: newUser.username, email: newUser.email } }); } catch (err) { // 处理Zod验证错误 if (err instanceof z.ZodError) { const errors = err.issues.map(issue => ({ field: issue.path[0], message: issue.message })); return res.status(400).json({ errors }); } // 处理Prisma唯一约束冲突(邮箱已存在) if (err instanceof PrismaClientKnownRequestError && err.code === "P2002") { return res.status(409).json({ message: "邮箱已被占用" }); } // 其他Prisma错误 if (err instanceof PrismaClientKnownRequestError) { return res.status(400).json({ message: "数据库操作失败" }); } // 通用服务器错误 res.status(500).json({ message: "服务器内部错误" }); } };
- (可选优化)调整
user.service.ts的参数为对象,更贴合Zod定义的类型:
import { prisma } from './prisma-client'; // 假设你有这个导出 import type { UserModel } from './user.model'; export const createUser = async (userData: UserModel) => { const user = await prisma.user.create({ data: userData, // 可选:指定返回字段,避免返回密码 select: { id: true, username: true, email: true, createdAt: true } }); return user; };
对应的控制器调用改为:
const newUser = await createUser({ username, email, password: hashedPassword });
内容的提问来源于stack exchange,提问作者kevin
相关产品推荐
相关产品推荐

