Node 如何在 Controller 层进行数据校验
liuian 2025-06-09 23:33 41 浏览
作者:山月行
转发链接:
https://mp.weixin.qq.com/s/UAU_Vpu3o53zqEAKDYgvQQ
前言
幽默风趣的后端程序员一般自嘲为 CURD Boy。CURD, 也就是对某一存储资源的增删改查,这完全是面向数据编程啊。
真好呀,面向数据编程,往往会对业务理解地更加透彻,从而写出更高质量的代码,造出更少的 BUG。既然是面向数据编程那更需要避免脏数据的出现,加强数据校验。否则,难道要相信前端的数据校验吗,毕竟前端数据校验直达用户,是为了 UI 层更友好的用户反馈。
数据校验层
后端由于重业务逻辑以及待处理各种数据,以致于分成各种各样的层级,以我经历过的后端项目就有分为 Controller、Service、Model、Helper、Entity 等各种命名的层,五花八门。但这里肯定有一个层称为 Controller,站在后端最上层直接接收客户端传输数据。
由于 Controller 层是服务器端中与客户端数据交互的最顶层,秉承着 Fail Fast 的原则,肩负着数据过滤器的功能,对于不合法数据直接打回去,如同秦琼与尉迟恭门神般威严。
数据校验同时衍生了一个半文档化的副产品,你只需要看一眼数据校验层,便知道要传哪些字段,都是些什么格式。
以下都是常见的数据校验,本文讲述如何对它们进行校验:
- required/optional
- 基本的数据校验,如 number、string、timestamp 及值需要满足的条件
- 复杂的数据校验,如 IP、手机号、邮箱与域名
const body = {
id,
name,
mobilePhone,
email
}
山月接触过一个没有数据校验层的后端项目,if/else 充斥在各种层级,万分痛苦,分分钟想重构。
JSON Schema
JSON Schema 基于 JSON 进行数据校验格式,并附有一份规范 json-schema.org[1],目前 (2020-08) 最新版本是 7.0。各种服务器编程语言都对规范进行了实现,如 go、java、php 等,当然伟大的 javascript 也有,如不温不火的 ajv[2]。
以下是校验用户信息的一个 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}#34;,
"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[3] 自称最强大的 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[4],前置数据校验到路由层,对前端传递来的 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 原理与实践[5]
const safe = require('safe-regex')
const re = /(x+x+)+y/
// 能跑死 CPU 的一个正则
re.test('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
// 使用 safe-regex 判断正则是否安全
safe(re) // false
数据校验,针对的大多是字符串校验,也会充斥着各种各样的正则表达式,保证正则表达式的安全相当紧要。safe-regex[6] 能够发现哪些不安全的正则表达式。
总结
- Controller 层需要进行统一的数据校验,可以采用 JSON Schema (Node 实现 ajv) 与 Joi
- JSON Schema 有官方规范及各个语言的实现,但语法繁琐,可使用校验功能更为强大的 Joi
- 进行字符串校验时,注意不安全的正则引起的性能问题
作者:山月行
转发链接:
https://mp.weixin.qq.com/s/UAU_Vpu3o53zqEAKDYgvQQ
相关推荐
- win10装机必备实用软件(win10电脑装机必备软件)
-
1、office大部分的版本如office2007、office2000、office2011、office2013、office2016、office365等都支持win10。2、需要注意...
- 迅雷无法下载的链接用什么下载
-
1.可以使用其他下载工具代替迅雷。2.迅雷可能无法下载的原因有很多,比如网络问题、软件故障等。其他下载工具可以提供类似的功能,但可能具有更好的稳定性和兼容性。3.一些常见的替代迅雷的下载工具包括...
- apple官方网站(apple官方网站旗舰店)
-
1、首先打开浏览器,输入https://www.apple.com/;2、即可浏览苹果官网。 苹果公司(AppleInc.)是美国一家高科技公司。由史蒂夫·乔布斯、斯蒂夫·沃兹尼亚克和罗·韦恩(R...
- 哪些手机用鸿蒙系统(都什么手机能用鸿蒙系统)
-
截至目前,国内有以下几款手机品牌可以装鸿蒙系统:1.华为:华为Mate40系列、P40系列、Mate30系列、MatePadPro系列等。2.荣耀:荣耀V40、荣耀30系列、荣耀X10系列等...
- 手机u盘读不出来了怎么修复(手机u盘读取不出来)
-
1、手机不支持OTG功能,所以将U盘连接到手机后,手机无法识别U盘的内容,因此显示不了;这种情况只能换台支持OTG功能的手机来连接U盘才行。2、手机支持OTG功能,但是使用的OTG线质量有问题导致无法...
- 笔记本散热器买哪种好(笔记本散热器买哪种好贴吧)
-
散热器有十大品牌:九州风神、超频三,酷冷至尊Tt、AVC、思民、捷冷、安钛克Antec、安耐美Enermax、海盗船Corsair。能位列十大品牌,每一种的质量和功能都有保障。、目前网上销量最高的是九...
-
- 打印机驱动一直安装失败(打印机驱动一直安装失败怎么办)
-
打印机驱动程序安装失败需要对电脑进行其他设置,详细步骤如下:1,在电脑桌面上找到【计算机】并用鼠标右击。2,右击后在出现的选项中找到【管理】选项并点击打开。3,接下里会进入到计算机控制台界面,在这里要根据自己的电脑选择64位或者32位,选择...
-
2026-01-14 12:55 liuian
- ctrl加谁是截图(ctrl和什么键可以截图)
-
第一种:Ctrl+PrScrn使用这个组合键截屏,获得的是整个屏幕的图片第二种:Alt+PrScrn这个组合键截屏,获得的结果是当前窗口的图片第三种:打开qq,使用快捷键Ctrl+...
- 技嘉主板bios设置启动顺序(技嘉主板bios设置启动顺序怎么设置)
-
启动顺序设置方法如下:1、重启电脑连续按[DEL]键进入BIOS设置,按DEL进入BIOS设置。2、按键盘方向键右键切换到BOOT选项,将windows10功能设置为"其它操作系统"...
-
- 目前台式电脑主机怎么选(台式主机选择)
-
每个人对电脑的性需要不同,因此根据自己家的家庭需要,选择合适的电脑即可。以下简单说明:1,双核处理器+2G内存+集成显卡+机械硬盘。性能满足上网、看电影、聊天、办公、玩象棋之类的小游戏。价格在2000以内可以买到;2,四核处理器+4G内存+...
-
2026-01-14 12:05 liuian
- 台式电脑如何用u盘重装系统(台式电脑如何用u盘重装系统应用)
-
1、重启电脑并进入BIOS;2、在BIOS中设置启动顺序,优先从U盘启动;3、从U盘启动,进入安装界面;4、选择安装语言、时区和键盘设置;5、选择安装方式,一般选择“清除整个硬盘并安装”;6、配置分区...
-
- stop0x0000007b蓝屏(stop0X0000007b蓝屏,修改注册表)
-
步骤/方式1将电脑送到当地的维修店步骤/方式2然后将师傅维修一下蓝屏的问题当电脑启动蓝屏出现错误代码0x0000007b时,首先我们将电脑重启,在开机时不停按启动热键进入到bios设置页面,进入页面后找到“IntegratedPeriphe...
-
2026-01-14 11:21 liuian
- 怎样设置默认打印机(打印机设置彩色打印在哪里设置)
-
步骤如下:1.单击Windows“开始”菜单,指向“设置”,单击“打印机”,打开“打印机”窗口。2.右键单击打印机图标,系统弹出快速菜单,单击其中的“设为默认值”。如果“打印机”窗口没有当前使用的打印...
- 一周热门
-
-
飞牛OS入门安装遇到问题,如何解决?
-
如何在 iPhone 和 Android 上恢复已删除的抖音消息
-
Boost高性能并发无锁队列指南:boost::lockfree::queue
-
大模型手册: 保姆级用CherryStudio知识库
-
用什么工具在Win中查看8G大的log文件?
-
如何在 Windows 10 或 11 上通过命令行安装 Node.js 和 NPM
-
威联通NAS安装阿里云盘WebDAV服务并添加到Infuse
-
Trae IDE 如何与 GitHub 无缝对接?
-
idea插件之maven search(工欲善其事,必先利其器)
-
如何修改图片拍摄日期?快速修改图片拍摄日期的6种方法
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
