内容简介:这个系列文章主旨就是通过写代码来入门,并不深入。只是记录我平时使用到了什么新的技术或插件的入门过程~
这个系列文章主旨就是通过写代码来入门,并不深入。只是记录我平时使用到了什么新的技术或插件的入门过程~
express-validator
最近写node端后台写的比较多,慢慢的发现前端呢转node端虽然没有那么难,但是有很多细节的东西还没有掌握,比如以前前后端分离的时候,对于一些需求模糊的表单场景,前端可能约束会很松,大部分的约束都是后端去做的,然后用户提交的信息某个字段不合法也是后端反馈给我们异常,前端再做处理。
因为后端直接接触的就是数据库,每一个字段都必须严格约束,所以对于接口字段的验证,特别是post(往数据库insert)的时候,验证必须严格,我们总不能每一个接口都自己写一套正则来进行校验吧,想一想也是,express庞大的社区肯定已经有类似的中间件了。去npm搜了一下关键字 express + validate 。映入眼帘的就是这个 —— express-validator。
// express-validator官网描述是一个基于validator.js封装的express中间件。 express-validator is a set of express.js middlewares that wraps validator.js validator and sanitizer functions. 复制代码
Getting Started
还是沿用第一节的套路,不管你三七二十一,先按照官网示例,跑通一个Demo,然后我在慢慢来弄~ 这里我依然节省时间,直接使用我之前写过的全栈脚手架 express-react-scaffold 来直接使用 express-validator 。
关于这个脚手架的文章在这里 新手搭建简洁的Node+React脚手架 ,正好也是我的第一篇文章,有很多小伙伴也点过赞,一直没时间维护,借此机会温故知新一下,简单回顾了一下,发现当时写的真心锉啊,借此机会小改一下吧~
其实对于后端接口字段校验,首先想到的就是表单提交了,因为对于GET请求,无论是query还是param,大部分校验工作前端来做就已经可以解决问题了,query和param的合法性通过了,一般后端也就不出问题了(当然,并不是说后端就不必校验了)。而post、put等这种涉及到操作数据库的请求,如果字段类型不匹配,就很容易发生未知错误,而且因为表单里不同表单项会有繁琐的校验规则,所以后端必须控制好~
以注册接口为例,跑第一个成功Demo
我们先来看一下以前的注册接口:
可以看到,需要三个字段,那么我们假设是这样的:
前端:
用户名:非空
邮箱:必须是邮箱类型
密码:非空
后端:
用户名:必须大于6位
邮箱:必须是邮箱类型
密码:必须大于6位
复制代码
从上面我们可以看出,前后端约束条件不同,也就是说可能存在前端输入合法而后端输入不合法的场景~
从文档的例子我们可以知道,express-validator的校验只需要在路由path和handler中间插入校验规则数组,我们来写一下。
// 原来的接口
// 用户注册接口
router.post('/register', (req, res) => {
User.findOne({ //查找是否存在
username: req.body.username,
},(err, user)=>{
if (err) {
res.send('server or db error');
} else {
if (user === null) {
const insertObj = {
username: req.body.username,
password: md5(req.body.password + MD5_SUFFIX),
email: req.body.email,
role: 10.0
};
const newUser = new User(insertObj);
newUser.save(insertObj, (err, doc) => {
if (err) {
res.json({ result: false, msg: '用户注册失败' });
} else {
console.log(doc);
res.json({ result: true, msg: '用户注册成功' });
}
});
} else {
res.json({ result: false, msg: '用户名已存在'});
}
}
});
});
复制代码
// 增加验证过后的接口
// 用户注册接口
router.post('/register', [
check('username').isLength({ min: 6 }),
check('email').isEmail(),
check('password').isLength({ min: 6 })
], (req, res) => {
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
User.findOne({ //查找是否存在
username: req.body.username,
},(err, user)=>{
if (err) {
res.send('server or db error');
} else {
if (user === null) {
const insertObj = {
username: req.body.username,
password: md5(req.body.password + MD5_SUFFIX),
email: req.body.email,
role: 10.0
};
const newUser = new User(insertObj);
newUser.save(insertObj, (err, doc) => {
if (err) {
res.json({ result: false, msg: '用户注册失败' });
} else {
console.log(doc);
res.json({ result: true, msg: '用户注册成功' });
}
});
} else {
res.json({ result: false, msg: '用户名已存在'});
}
}
});
});
复制代码
好,然后我们来试一下:
测试用例: 用户名 - aaa, 用户邮箱 - aaa@126.com, 密码 - aaa 复制代码
如图,可以看到,前端通过之后,后端没通过,说明我们写的内容生效了。所以!我们的第一个validate demo也就写完了。
知其然也知其所以然
上面第一个例子虽然生效了,但是我其实还是有点稀里糊涂,相信小伙伴们也一样,凭啥?为啥就那么加就通过了?别急,我们一步一步来。 先来看看代码:
// 校验内容部分
[
check('username').isLength({ min: 6 }),
check('email').isEmail(),
check('password').isLength({ min: 6 })
]
// 校验结果部分
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
复制代码
校验内容部分就很简单了,无非就是约束条件,现在很简单,以后肯能会变得很复杂,但是不是要考虑的。然后就来看结果部分了,可以看到,通过 validationResult(req) 获取校验结果,我们将它打印出来看一看:
{
isEmpty: [Function],
array: [Function],
mapped: [Function],
formatWith: [Function],
throw: [Function]
}
复制代码
可以看到校验结果返回了几个api,我们来猜一猜或者打印一下就知道了,因为代码里只用到了 isEmpty()和array() ,而且意思很明显,就是如果 errors.isEmpty() 为真,就表示校验通过,所以用脑袋想一想 isEmpty() 应该是bool类型返回校验是否通过,如果为假就是校验不通过,然后把不通过的数组信息返回给我们。我们就打印一下二者:
// isEmpty
errors.isEmpty() ====> false // 返回的是bool值,表示结果
errors.array()
[
{
location: 'body',
param: 'username',
value: 'aaa',
msg: 'Invalid value,
},
{
location: 'body',
param: 'password',
value: 'aaa',
msg: 'Invalid value'
}
]
复制代码
可以看到,errors.array()返回的是校验不通过的字段的数组以及对应的信息。所以关于整体的校验流程基本掌握了。接下来就是巩固加深提高的过程了~
学习使用express-validator的各种API
上面基本了解了如何在后端使用express-validator,但是有一些点还是不理解:
比如:在handler前面加上校验数组,数组的内容是我们写的字段,那么字段如果写错呢? 再比如:他怎么知道我想校验的字段在哪?是query还是param还是body还是header呢? 复制代码
带着疑惑,我们在看读文档,等一下,读文档之前,其实我们可以再看看上面的错误数组:
[
{
location: 'body',
param: 'username',
value: 'aaa',
msg: 'Invalid value,
},
{
location: 'body',
param: 'password',
value: 'aaa',
msg: 'Invalid value'
}
]
复制代码
嗯,很明显,错误数组对于我们的字段判断是正确的,location字段它定位的是body,确实,我们的post接口确实将数据放到了body里。因此,应该是express-validator会check所有与我们规定值相匹配的req字段吧,带着疑问去查阅一下文档~
还真是,我们的check还就是把能匹配的都匹配一下,那么问题又来了,这么是不是效率会很低,既然是我们自己写的,我们肯定知道在哪里去找,能提升效率啊~好吧,我都想到了,人家作者能想不到吗?
check API
- 限定范围类(check, body, query, header, param, cookie)
check API就是校验各种规则的api,其中包括各种封装好的校验函数,如: isString()、isInt()、isLength({}) 等,除此之外还有很多限定范围的api,如图
可见,也就是上述我们说的问题,我们可以通过约定检索范围提升效率,比如register的接口,我们只需要检验body的字段就行了,那么就可以使用body来进行check,我们来试一试:
const { body, validationResult } = require('express-validator/check');
[
body('username').isLength({ min: 6 }),
body('email').isEmail(),
body('password').isLength({ min: 6 })
]
复制代码
换完过后,结果依然成立,其他类似的check API也类似了,就是你校验的字段在哪里就用对应API去检验就好了,提升效率~。
- 自定义限定范围(buildCheckFunction)
出了上述限定范围,我们还可以通过 buildCheckFunction 来自定义范围,比如我们校验某个字段id只有在body或query才有效,并且是UUID类型的数据,代码如下:
const { buildCheckFunction } = require('express-validator/check');
const checkBodyAndQuery = buildCheckFunction(['body', 'query']);
app.put('/update-product', [
// id must be either in req.body or req.query, and must be an UUID
checkBodyAndQuery('id').isUUID()
], productUpdateHandler)
复制代码
-
校验结果validationResult(req) 这个很简单了,就是把
express req传进去,然后返回我们上面提到的那个error对象~这个就不多做介绍了,因为官方也没有详细介绍。 -
oneOf(validationChains[, message])
这个也很简单,就是只要几个条件之中的一个满足,我们就认为校验是通过的~这个场景说实话我还确实没想过哪里能用到,不过还是试一试,我们在登录接口尝试,将用户名验证是否是字符串,密码验证变成是否是数组,这肯定是个假命题,不过最后结果不出意外是通过,因为用户名是正确的:
// router login - 登录接口
oneOf([
body('username').isString(),
body('password').isArray()
])
最后打印出来的结果:validationResult(req).isEmpty() === true。
复制代码
Validation Result API
验证结果的API,也算是最重要的API了,因为校验通过不通过,要返回给客户端什么信息,都是通过这个API获取的。
validationResult(req)
-
isEmpty()
这个上面说过了,就是返回一个bool值,表示check部分是否有错,有错就是
false,没错就是true。一般使用就是:if (!validationResult(req).isEmpty()) { res.status(错误码).json({ 错误信息 }); } 复制代码 -
formatWith(formatter) 这个api意义我个人觉得也不是很大,不过算是锦上添花吧。就是可以自定义错误信息格式。
app.post('/create-user', yourValidationChains, (req, res, next) => { const errorFormatter = ({ location, msg, param, value, nestedErrors }) => { // 定义返回错误的样式,存入array数组 return `${location}[${param}]: ${msg}`; }; const result = validationResult(req).formatWith(errorFormatter); if (!result.isEmpty()) { // { errors: [ "body[password]: must be at least 10 chars long" ] } return res.json({ errors: result.array() }); } ... }); 复制代码 -
array([options])
存放返回的错误信息,参数可以设置是否只返回所有错误的第一条,默认返回所有错误。
Default :
{ onlyFirstError: false },如果想要默认返回第一条,设置该参数为true即可 -
mapped()
这个API跟
isArray()基本一致,就是返回错误,不过isArray()和mapped()的区别就是一个返回的是数组,一个返回的是对象,mapped()返回的是key和value键值对,value跟array数组返回的内容一致。// 假设我把login接口的username和password的check验证都改成isArray()。 [ body('username').isArray(), body('password').isArray() ], // validationResult(req).mapped() { username: { location: 'body', param: 'username', value: 'luffy', msg: 'Invalid value' }, password: { location: 'body', param: 'password', value: '123456', msg: 'Invalid value' } } 复制代码 -
throw()
使用这个api就是不使用
isEmpty(),通过throw()一个error来返回错误。try { validationResult(req).throw(); // Oh look at ma' success! All validations passed! } catch (err) { console.log(err.mapped()); // Oh noes! } 复制代码
filter API + Validation Chain API
其实上面两个API我觉得已经足够了,基本满足业务场景了,不过 express-validator 还提供很多更完善的功能。下面这些API就简单过一下吧,如果有我觉得能用得到的,就写个例子,我觉得check就够用了~哈哈。
-
sanitize系列
这个与check API类似,也可以限定范围和自定义范围,用处与check API不一样,check API是检验对应参数是否合法,sanitize系列API是可以帮我们提前做一个转换工作,比如我们后台要求的是数字
1,但是前端传过来的是字符串'1',就可以通过sanitize系列API进行转换。const { buildSanitizeFunction } = require('express-validator/filter'); const sanitizeBodyAndQuery = buildSanitizeFunction(['body', 'query']); app.put('/update-product', [ // 限定范围在body和query内,将id转换成整型 sanitizeBodyAndQuery('id').toInt() ], productUpdateHandler) 复制代码 -
validation chain
这个也不算新的API,应该就是特性吧,感觉跟jQuery一样,不断的链式调用,每一次调用都返回新的结果~
// 检验weekday字段是否不在['sunday', 'saturday']内。 check('weekday').not().isIn(['sunday', 'saturday']) 复制代码 -
withMessage
这个不是新API,官方是列在Validation Chain API里的,不过我觉得这个是个很有用的API,所有就单独拿出来说一下,就是错误消息可以自定义,我们可以设置返回消息放在里面。
// 验证部分 [ body('username').isArray().withMessage('username类型不正确'), body('password').isArray().withMessage('password类型不正确') ], // 结果部分 { username: { location: 'body', param: 'username', value: 'luffy', msg: 'username类型不正确' }, password: { location: 'body', param: 'password', value: '123456', msg: 'password类型不正确' } } 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 使用 Cosign 进行镜像签名和校验
- 我是怎么进行SpringMVC参数校验的?
- Go 使用validator进行后端数据校验
- 封装axios请求并对提交参数进行校验
- Spring AOP @PathVariable和@RequestParam 参数进行校验(valid)
- Echo 系列教程 — 定制篇2:自定义 Validator,进行输入校验
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Introduction to Programming in Java
Robert Sedgewick、Kevin Wayne / Addison-Wesley / 2007-7-27 / USD 89.00
By emphasizing the application of computer programming not only in success stories in the software industry but also in familiar scenarios in physical and biological science, engineering, and appli......一起来看看 《Introduction to Programming in Java》 这本书的介绍吧!