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 相关文章推荐
将HTML自动转为JS代码
Jun 26 Javascript
PHP中CURL的几个经典应用实例
Jan 23 Javascript
详细解读JavaScript编程中的Promise使用
Jul 27 Javascript
JavaScript实现斗地主游戏的思路
Feb 29 Javascript
jQuery实现TAB选项卡切换特效简单演示
Mar 04 Javascript
基于Bootstrap的后台管理面板 Bootstrap Metro Dashboard
Jun 17 Javascript
微信小程序前端源码逻辑和工作流
Sep 25 Javascript
微信小程序 实战实例开发流程详细介绍
Jan 05 Javascript
vue 封装自定义组件之tabal列表编辑单元格组件实例代码
Sep 07 Javascript
angularJs-$http实现百度搜索时的动态下拉框示例
Feb 27 Javascript
vue项目打包后打开页面空白解决办法
Jun 29 Javascript
解决新建一个vue项目过程中遇到的问题
Oct 22 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
完美的2个php检测字符串是否是utf-8编码函数分享
2014/07/28 PHP
学习ExtJS(二) Button常用方法
2009/10/07 Javascript
javascript demo 基本技巧
2009/12/18 Javascript
jquery学习笔记 用jquery实现无刷新登录
2011/08/08 Javascript
JavaScript异步调用定时方法并停止该方法实现代码
2012/03/16 Javascript
javascript函数中参数传递问题示例探讨
2014/07/31 Javascript
Node.js中使用计时器定时执行函数详解
2014/08/15 Javascript
JavaScript学习笔记之Cookie对象
2015/01/22 Javascript
js前端实现多图图片上传预览的两个方法(推荐)
2016/11/18 Javascript
vue购物车插件编写代码
2017/11/27 Javascript
jQuery实现简单的下拉菜单导航功能示例
2017/12/07 jQuery
javascript 数组精简技巧小结
2020/02/26 Javascript
Node.js API详解之 Error模块用法实例分析
2020/05/14 Javascript
JS定时器如何实现提交成功提示功能
2020/06/12 Javascript
antd的select下拉框因为数据量太大造成卡顿的解决方式
2020/10/31 Javascript
[02:16]深扒TI7聊天轮盘语音出处2
2017/05/11 DOTA
[58:00]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Elephant BO3 第二场 2月7日
2021/03/11 DOTA
在Python中操作文件之read()方法的使用教程
2015/05/24 Python
Python中操作文件之write()方法的使用教程
2015/05/25 Python
Python常用的内置序列结构(列表、元组、字典)学习笔记
2016/07/08 Python
python查询mysql,返回json的实例
2018/03/26 Python
Python实现使用卷积提取图片轮廓功能示例
2018/05/12 Python
解决安装python库时windows error5 报错的问题
2018/10/21 Python
Python 3.3实现计算两个日期间隔秒数/天数的方法示例
2019/01/07 Python
PyTorch中 tensor.detach() 和 tensor.data 的区别详解
2020/01/06 Python
Python matplotlib修改默认字体的操作
2020/03/05 Python
浅谈Tensorflow加载Vgg预训练模型的几个注意事项
2020/05/26 Python
HTML5的video标签的浏览器兼容性增强方案分享
2016/05/19 HTML / CSS
美国护肤咨询及美容产品电商:Askderm
2017/02/24 全球购物
东芝官网商城:还原日式美学,打造美好生活
2018/12/27 全球购物
美国户外烹饪产品购物网站:Outdoor Cooking
2020/01/10 全球购物
致标枪运动员加油稿
2014/02/15 职场文书
材料物理专业求职信
2014/09/01 职场文书
代办社保委托书范文
2014/10/06 职场文书
小学生作文评语集锦
2014/12/25 职场文书
党组织关系的介绍信模板
2019/06/21 职场文书