在Node.js中使用Joi验证请求值的唯一性
如何仅通过Joi实现注册接口的Email唯一性校验
要在Joi中纯实现Email唯一性校验,核心是利用Joi的异步自定义校验规则,直接在Joi的校验流程中完成数据库查询校验,无需额外的validateInDatabase函数或mongoose Schema校验。以下是具体实现步骤:
1. 修改Joi校验规则,添加Email异步唯一性校验
在你的userValidation.createUser规则中,给email字段添加异步自定义校验函数,直接查询数据库判断Email是否已存在:
exports.createUser = { body: Joi.object().keys({ email: Joi.string().required().email() .custom(async (value, helpers) => { // 查询数据库中是否存在该邮箱 const existingUser = await User.findOne({ email: value }); if (existingUser) { // 返回自定义错误类型,方便后续处理错误消息 return helpers.error('email.exists', { value }); } return value; }, 'Email唯一性校验'), // 第二个参数是校验规则的描述,可选 password: Joi.string().required().custom(password), phoneNumber: Joi.string().min(10).required(), }), };
2. 改造校验中间件,支持异步校验
原有的validate中间件使用的是Joi同步的validate方法,无法处理异步校验逻辑,需要改成使用validateAsync并将中间件设为异步函数:
const validate = (schema) => async (req, res, next) => { const validSchema = pick(schema, ["params", "query", "body"]); const object = pick(req, Object.keys(validSchema)); try { // 使用validateAsync执行异步校验 const value = await Joi.compile(validSchema) .prefs({ errors: { label: "key" }, abortEarly: false }) .validateAsync(object); Object.assign(req, value); next(); } catch (error) { // 自定义错误消息,把Joi返回的错误类型转换成友好提示 const errorMessage = error.details .map(details => { if (details.type === 'email.exists') { return `邮箱 ${details.context.value} 已被注册`; } return details.message; }) .join(", "); return res.status(400).json({ error: errorMessage }); } };
3. 简化注册控制器,移除冗余校验
现在Email的唯一性已经在Joi中间件中完成校验,控制器里可以去掉validateInDatabase的调用:
exports.register = catchAsync(async (req, res) => { try { var user = await userService.create(req.body); var token = await sendVerification(user); return res.status(201).json({ user, token }); } catch (error) { // 处理数据库保存时的其他重复错误(比如手机号重复,若未在Joi中添加校验) if (error.code === 11000) { return res.status(409).json({ error: "手机号已被注册" }); } return res.status(400).json({ message: error.message }); } });
补充说明
- 如果需要同时校验手机号的唯一性,只需按照Email的方式给
phoneNumber字段添加对应的异步自定义校验规则即可。 - 异步校验会增加一次数据库查询,但能让参数校验逻辑完全统一在Joi流程中,避免分散在多个地方。
内容的提问来源于stack exchange,提问作者John Oliver
相关产品推荐
相关产品推荐

