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

结合Zod与Prisma做用户输入验证遇问题:验证失效、错误未捕获

解决Zod验证失效与Prisma错误未捕获问题

一、Zod验证未生效的解决

你只定义了Zod验证规则,但从来没在控制器里调用验证逻辑,自然不会生效。

修复步骤:

  1. 在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 }; // 新增导出
  1. 在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;另外错误处理分支太窄,没覆盖所有情况。

修复步骤:

  1. 给createUser调用加await,确保异步错误能被捕获:
// 原代码:createUser(username, email, String(hashedPassword));
// 修改为:
const newUser = await createUser(username, email, hashedPassword);
  1. 完善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: "服务器内部错误" });
  }
};
  1. (可选优化)调整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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:05:59