目录
Node.js中Joi的详细用法
Joi 在 Node.js 中的详细用法
Joi 是一个强大的 JavaScript 对象模式验证库,常用于 Node.js 应用中验证和转换数据。下面详细介绍 Joi 的用法。
安装
npm install joi
基本用法
1. 基本验证
const Joi = require('joi');
// 定义 schema
const schema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')),
repeat_password: Joi.ref('password'),
email: Joi.string().email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } })
}).with('password', 'repeat_password');
// 验证数据
const { error, value } = schema.validate({
username: 'abc',
password: 'password123',
repeat_password: 'password123',
email: 'abc@example.com'
});
if (error) {
console.error(error.details);
} else {
console.log('验证通过', value);
}
2. 数据类型验证
字符串验证
Joi.string()
.alphanum() // 只允许字母数字字符
.min(3) // 最小长度3
.max(30) // 最大长度30
.required() // 必填字段
.trim() // 自动去除前后空格
.lowercase() // 转换为小写
.uppercase() // 转换为大写
.regex(/^[a-z]+$/) // 正则匹配
数字验证
Joi.number()
.integer() // 必须是整数
.min(1) // 最小值1
.max(10) // 最大值10
.precision(2) // 小数点后2位
.positive() // 必须是正数
.negative() // 必须是负数
布尔值验证
Joi.boolean()
.truthy('yes') // 将'yes'视为true
.falsy('no') // 将'no'视为false
日期验证
Joi.date()
.min('1-1-2000') // 最小日期
.max('now') // 最大日期为现在
.iso() // ISO格式
数组验证
Joi.array()
.items(Joi.string(), Joi.number()) // 数组元素可以是字符串或数字
.length(5) // 数组长度必须为5
.min(1) // 最小长度1
.max(10) // 最大长度10
.unique() // 元素必须唯一
对象验证
Joi.object({
name: Joi.string(),
age: Joi.number()
})
.and('name', 'age') // name和age必须同时存在
.or('name', 'age') // name或age必须存在一个
.xor('name', 'age') // name或age必须存在一个但不能同时存在
.pattern(/^a/, Joi.string()) // 所有以a开头的属性必须是字符串
3. 高级验证
条件验证
const schema = Joi.object({
isAdmin: Joi.boolean(),
accessLevel: Joi.when('isAdmin', {
is: true,
then: Joi.number().valid(1, 2, 3),
otherwise: Joi.number().valid(4, 5)
})
});
自定义验证
const schema = Joi.object({
password: Joi.string().custom((value, helpers) => {
if (value.length < 8) {
return helpers.error('password.too.short');
}
if (!/[A-Z]/.test(value)) {
return helpers.error('password.no.uppercase');
}
return value;
}, '密码验证')
}).messages({
'password.too.short': '密码长度至少8个字符',
'password.no.uppercase': '密码必须包含至少一个大写字母'
});
引用其他字段值
const schema = Joi.object({
password: Joi.string().required(),
confirmPassword: Joi.string().valid(Joi.ref('password')).required()
});
4. 错误处理
const { error, value } = schema.validate(data, {
abortEarly: false, // 不遇到第一个错误就停止,收集所有错误
allowUnknown: true, // 允许未知键
stripUnknown: true, // 移除未知键
convert: true // 尝试类型转换
});
if (error) {
// 错误详情
error.details.forEach((detail) => {
console.log(detail.message);
console.log(detail.path); // 错误路径
console.log(detail.type); // 错误类型
});
// 自定义错误消息
const errors = error.details.map(detail => ({
field: detail.path.join('.'),
message: detail.message
}));
}
5. 扩展和自定义
创建自定义验证规则
const customJoi = Joi.extend((joi) => {
return {
type: 'string',
base: joi.string(),
messages: {
'string.oddLength': '{{#label}} 长度必须是奇数',
},
validate(value, helpers) {
if (value.length % 2 === 0) {
return { value, errors: helpers.error('string.oddLength') };
}
}
};
});
const schema = customJoi.string().oddLength();
6. 实用方法
默认值
Joi.string().default('default value')
可选/必填
Joi.string().optional()
Joi.string().required()
允许/禁止值
Joi.string().valid('value1', 'value2') // 只允许特定值
Joi.string().invalid('value1', 'value2') // 禁止特定值
标签和描述
Joi.string().label('用户名').description('用户的登录名')
7. 数组和对象的高级用法
数组元素验证
Joi.array().items(
Joi.object({
id: Joi.number().required(),
name: Joi.string().required()
})
)
对象模式匹配
Joi.object({
a: Joi.string(),
b: Joi.number()
}).pattern(Joi.string().pattern(/^x/), Joi.boolean())
8. 异步验证
const schema = Joi.object({
username: Joi.string().external(async (value, helpers) => {
const exists = await checkUsernameExists(value);
if (exists) {
return helpers.error('username.exists');
}
return value;
})
});
try {
const value = await schema.validateAsync({ username: 'test' });
} catch (err) {
console.error(err);
}
实际应用示例
用户注册验证
分页查询参数验证
const userSchema = Joi.object({
username: Joi.string()
.alphanum()
.min(3)
.max(30)
.required()
.label('用户名')
.messages({
'string.empty': '用户名不能为空',
'string.min': '用户名长度不能少于3个字符',
'string.max': '用户名长度不能超过30个字符'
}),
password: Joi.string()
.pattern(new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})'))
.required()
.label('密码')
.messages({
'string.pattern.base': '密码必须包含大小写字母、数字和特殊字符,且长度至少8位'
}),
email: Joi.string()
.email({ minDomainSegments: 2 })
.required()
.label('电子邮箱'),
birth_year: Joi.number()
.integer()
.min(1900)
.max(2023)
.label('出生年份'),
roles: Joi.array()
.items(Joi.string().valid('user', 'admin', 'editor'))
.default(['user'])
.label('角色'),
metadata: Joi.object()
.unknown()
.label('元数据'),
createdAt: Joi.date()
.default(Date.now)
.label('创建时间')
}).options({ abortEarly: false });
const paginationSchema = Joi.object({
page: Joi.number()
.integer()
.min(1)
.default(1)
.label('页码'),
pageSize: Joi.number()
.integer()
.min(1)
.max(100)
.default(10)
.label('每页数量'),
sort: Joi.string()
.valid('asc', 'desc')
.default('desc')
.label('排序方式'),
search: Joi.string()
.trim()
.max(100)
.allow('')
.label('搜索关键词')
});
最佳实践
- 复用验证模式:将常用的验证模式提取为可复用的组件
- 详细错误消息:提供有意义的错误消息
- 尽早验证:在请求处理流程的早期进行验证
- 区分开发和生产:在生产环境中可能不需要返回详细的验证错误
- 结合Express中间件:创建验证中间件
function validate(schema, property = 'body') {
return (req, res, next) => {
const { error, value } = schema.validate(req[property], {
abortEarly: false,
allowUnknown: false
});
if (error) {
const errors = error.details.map(detail => ({
field: detail.path.join('.'),
message: detail.message
}));
return res.status(400).json({ errors });
}
req[property] = value;
next();
};
}
// 使用示例
router.post('/users', validate(userSchema), userController.create);
Joi 提供了强大而灵活的验证功能,可以帮助你确保应用程序数据的完整性和一致性。




