Node在Controller层进行数据校验的过程详解


Posted in Javascript onAugust 28, 2020

前言

幽默风趣的后端程序员一般自嘲为 CURD Boy。CURD, 也就是对某一存储资源的增删改查,这完全是面向数据编程啊。

真好呀,面向数据编程,往往会对业务理解地更加透彻,从而写出更高质量的代码,造出更少的 BUG。既然是面向数据编程那更需要避免脏数据的出现,加强数据校验。否则,难道要相信前端的数据校验吗,毕竟前端数据校验直达用户,是为了 UI 层更友好的用户反馈。

数据校验层

后端由于重业务逻辑以及待处理各种数据,以致于分成各种各样的层级,以我经历过的后端项目就有分为 Controller、Service、Model、Helper、Entity 等各种命名的层,五花八门。但这里肯定有一个层称为 Controller,站在后端最上层直接接收客户端传输数据。

由于 Controller 层是服务器端中与客户端数据交互的最顶层,秉承着 Fail Fast 的原则,肩负着数据过滤器的功能,对于不合法数据直接打回去,如同秦琼与尉迟恭门神般威严。

数据校验同时衍生了一个半文档化的副产品,你只需要看一眼数据校验层,便知道要传哪些字段,都是些什么格式。

以下都是常见的数据校验,本文讲述如何对它们进行校验:

  1. required/optional
  2. 基本的数据校验,如 number、string、timestamp 及值需要满足的条件
  3. 复杂的数据校验,如 IP、手机号、邮箱与域名
const body = {
 id,
 name,
 mobilePhone,
 email
}

山月接触过一个没有数据校验层的后端项目,if/else 充斥在各种层级,万分痛苦,分分钟向重构。

JSON Schema

JSON Schema 基于 JSON 进行数据校验格式,并附有一份规范 json-schema.org,目前 (2020-08) 最新版本是 7.0。各种服务器编程语言都对规范进行了实现,如 go、java、php 等,当然伟大的 javascript 也有,如不温不火的ajv。

以下是校验用户信息的一个 Schema,可见语法复杂与繁琐:

{
 "$schema": "http://json-schema.org/draft-04/schema#",
 "title": "User",
 "description": "用户信息",
 "type": "object",
 "properties": {
 "id": {
 "description": "用户 ID",
 "type": "integer"
 },
 "name": {
 "description": "用户姓名",
 "type": "string"
 },
 "email": {
 "description": "用户邮箱",
 "type": "string",
 "format": "email",
 "maxLength": 20
 },
 "mobilePhone": {
 "description": "用户手机号",
 "type": "string",
 "pattern": "^(?:(?:\+|00)86)?1[3-9]\d{9}$",
 "maxLength": 15
 }
 },
 "required": ["id", "name"]
}

对于复杂的数据类型校验,JSON Schema 内置了以下 Format,方便快捷校验

  • Dates and times
  • Email addresses
  • Hostnames
  • IP Addresses
  • Resource identifiers
  • URI template
  • JSON Pointer
  • Regular Expressions

对于不在内置 Format 中的手机号,使用 ajv.addFormat 可手动添加 Format

ajv.addFormat('mobilePhone', (str) => /^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(str));

Joi

joi 自称最强大的 JS 校验库,在 github 也斩获了一万六颗星星。相比 JSON Schema 而言,它的语法更加简洁并且功能强大。

The most powerful data validation library for JS

完成相同的校验,仅需要更少的代码,并能够完成更加强大的校验。以下仅做示例,更多示例请前往文档。

const schema = Joi.object({
 id: Joi.number().required(),
 name: Joi.number().required(),
 email: Joi.string().email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }),
 mobilePhone: Joi.string().pattern(/^(?:(?:\+|00)86)?1[3-9]\d{9}$/),

 password: Joi.string().pattern(/^[a-zA-Z0-9]{3,30}$/),
 // 与 password 相同的校验
 repeatPassword: Joi.ref('password'),
})
 // 密码与重复密码需要同时发送
 .with('password', 'repeat_password');
 // 邮箱与手机号提供一个即可
 .xor('email', 'mobilePhone')

数据校验与路由层集成

由于数据直接从路由传递,因此 koajs 官方基于 joi 实现了一个joi-router,前置数据校验到路由层,对前端传递来的 query、body 与 params 进行校验。

joi-router 也同时基于 co-body 对前端传输的各种 content-type 进行解析及限制。如限制为 application/json,也可在一定程度上防止 CSRF 攻击。

const router = require('koa-joi-router');
const public = router();

public.route({
 method: 'post',
 path: '/signup',
 validate: {
 header: joiObject,
 query: joiObject,
 params: joiObject,
 body: joiObject,
 maxBody: '64kb',
 output: { '400-600': { body: joiObject } },
 type: 'json',
 failure: 400,
 continueOnError: false
 },
 pre: async (ctx, next) => {
 await checkAuth(ctx);
 return next();
 },
 handler: async (ctx) => {
 await createUser(ctx.request.body);
 ctx.status = 201;
 },
});

正则表达式与安全正则表达式

山月在一次排查性能问题时发现,一条 API 竟在数据校验层耗时过久,这是我未曾想到的。而问题根源在于不安全的正则表达式,那什么叫做不安全的正则表达式呢?

比如下边这个能把 CPU 跑挂的正则表达式就是一个定时炸弹,回溯次数进入了指数爆炸般的增长。

可以参考文章 浅析 ReDos 原理与实践

const safe = require('safe-regex')
const re = /(x+x+)+y/

// 能跑死 CPU 的一个正则
re.test('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')

// 使用 safe-regex 判断正则是否安全
safe(re) // false

数据校验,针对的大多是字符串校验,也会充斥着各种各样的正则表达式,保证正则表达式的安全相当紧要。safe-regex 能够发现哪些不安全的正则表达式。

总结

  1. Controller 层需要进行统一的数据校验,可以采用 JSON Schema (Node 实现 ajv) 与 Joi
  2. JSON Schema 有官方规范及各个语言的实现,但语法繁琐,可使用校验功能更为强大的 Joi
  3. 进行字符串校验时,注意不安全的正则引起的性能问题

到此这篇关于Node在Controller层进行数据校验的文章就介绍到这了,更多相关Node在Controller层数据校验内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
基于Jquery的开发个代阴影的对话框效果代码
Jul 28 Javascript
12行javascript代码绘制一个八卦图
Apr 02 Javascript
AngularJS 服务详细讲解及示例代码
Aug 17 Javascript
vue的props实现子组件随父组件一起变化
Oct 27 Javascript
jQuery实现弹出窗口弹出div层的实例代码
Jan 09 Javascript
angularJs的ng-class切换class
Jun 23 Javascript
JS实现分页浏览横向图片(类轮播)实例代码
Nov 06 Javascript
基于vue实现分页效果
Nov 06 Javascript
微信小程序使用form表单获取输入框数据的实例代码
May 17 Javascript
element-ui的回调函数Events的用法详解
Oct 16 Javascript
解决Vue中的生命周期beforeDestory不触发的问题
Jul 21 Javascript
Openlayers+EasyUI Tree动态实现图层控制
Sep 28 Javascript
Postman无法正常返回结果问题解决
Aug 28 #Javascript
Vue+Element UI 树形控件整合下拉功能菜单(tree + dropdown +input)
Aug 28 #Javascript
vue自定义指令和动态路由实现权限控制
Aug 28 #Javascript
vue 动态给每个页面添加title、关键词和描述的方法
Aug 28 #Javascript
vue-cli+webpack项目打包到服务器后,ttf字体找不到的解决操作
Aug 28 #Javascript
vue select 获取value和lable操作
Aug 28 #Javascript
VSCode 添加自定义注释的方法(附带红色警戒经典注释风格)
Aug 27 #Javascript
You might like
php 文件上传系统手记
2009/10/26 PHP
Parse正式发布开源PHP SDK
2014/08/11 PHP
ThinkPHP无限级分类原理实现留言与回复功能实例
2014/10/31 PHP
PHP中获取文件创建日期、修改日期、访问时间的方法
2016/11/05 PHP
HTML页面如何象ASP一样接受参数
2007/02/07 Javascript
Javascript数组的排序 sort()方法和reverse()方法
2012/06/04 Javascript
给artDialog 5.02 增加ajax get功能详细介绍
2012/11/13 Javascript
Jquery图片延迟加载插件jquery.lazyload.js的使用方法
2014/05/21 Javascript
javascript中定义类的方法详解
2015/02/10 Javascript
JavaScript中的继承方式详解
2015/02/11 Javascript
详细探究ES6之Proxy代理
2016/07/22 Javascript
DOM中事件处理概览与原理的全面解析
2016/08/16 Javascript
js实现放大镜特效
2017/05/18 Javascript
jQuery选择器中的特殊符号处理方法
2017/09/08 jQuery
js限制输入框只能输入数字(onkeyup触发)
2018/09/28 Javascript
Nuxt升级2.0.0时出现的问题(小结)
2018/10/08 Javascript
webpack配置proxyTable时pathRewrite无效的解决方法
2018/12/13 Javascript
D3.js 实现带伸缩时间轴拓扑图的示例代码
2020/01/20 Javascript
jQuery+css实现的点击图片放大缩小预览功能示例【图片预览 查看大图】
2020/05/29 jQuery
详解vue-cli项目在IE浏览器打开报错解决方法
2020/12/10 Vue.js
python 基础学习第二弹 类属性和实例属性
2012/08/27 Python
Python多线程学习资料
2012/12/19 Python
Python下的subprocess模块的入门指引
2015/04/16 Python
Python中元组,列表,字典的区别
2017/05/21 Python
itchat和matplotlib的结合使用爬取微信信息的实例
2017/08/25 Python
Python中将变量按行写入txt文本中的方法
2018/04/03 Python
Python中遍历列表的方法总结
2019/06/27 Python
Python通过正则库爬取淘宝商品信息代码实例
2020/03/02 Python
解决python父线程关闭后子线程不关闭问题
2020/04/25 Python
python 实现倒计时功能(gui界面)
2020/11/11 Python
CSS3教程(1):什么是CSS3
2009/04/02 HTML / CSS
请描述一下”is a”关系和”has a”关系
2015/02/03 面试题
财务个人年度总结范文
2015/02/26 职场文书
手机销售员岗位职责
2015/04/11 职场文书
社区节水倡议书
2015/04/29 职场文书
python如何查找列表中元素的位置
2022/05/30 Python