作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Andrej是一名屡获殊荣的全栈开发者,曾为Spotify等全球品牌开发项目, Gartner, 康卡斯特公司, 或幻想.
在API中执行的基本任务之一是数据验证. 在本文中, 我将向您展示如何为数据添加无懈可击的验证,同时返回格式良好的数据.
中执行自定义数据验证 节点.js 既不容易又不迅速. 为了覆盖任何类型的数据,您需要编写许多功能. 虽然我已经尝试了一些节点.Js表单数据库-用于两者 快运和Koa-他们从来没有满足过我项目的需要. 扩展库存在问题,库不能处理复杂的数据结构或异步验证.
这就是为什么我最终决定编写自己的小而强大的表单验证库的原因 datalize. 它是可扩展的,因此您可以在任何项目中使用它并根据您的需求对其进行定制. 它验证请求的主体、查询或参数. 它还支持 异步
过滤器和复杂的JSON结构,如 数组 or 嵌套对象.
Datalize可以通过npm安装:
NPM install——save datalize
要解析请求体,应该使用单独的库. 如果你还没有用过,我建议你使用 koa-body 为Koa或 分析体 为表达.
您可以将本教程应用于已经设置好的HTTP API服务器,或者使用下面的简单代码 Koa HTTP服务器 code.
const Koa = require(' Koa ');
const bodyParser = require('koa-body');
const 应用程序 = new Koa();
Const 路由器 = new (require('koa-路由器'))();
//帮助返回路由中的错误
应用程序.上下文.错误= 函数(code, obj) {
这.状态=代码;
这.Body = obj;
};
//添加koa-body中间件来解析JSON和form-data body
应用程序.使用(bodyParser ({
enableTypes: ['json', 'form'],
多部分:没错,
强大的:{
maxFileSize: 32 * 1024 * 1024;
}
}));
/ /路线...
//将定义的路由作为中间件连接到Koa
应用程序.使用(路由器.路线());
//我们的应用程序将监听端口3000
应用程序.听(3000);
控制台.log('🌍API监听3000');
但是,这不是您应该使用的生产设置 日志记录、执行 授权、处理 错误等.),但是这几行代码可以很好地用于我将向您展示的示例.
注意:所有代码示例都使用Koa,但是数据验证代码也适用于Express. datalize库还提供了实现Express表单验证的示例.
假设您有一个Koa或Express web服务器和API中的一个端点,该端点在数据库中创建具有多个字段的用户. 有些字段是必需的, 有些只能有特定的值,或者必须格式化为正确的类型.
你可以这样写简单的逻辑:
/**
* @api {post} /创建用户
* ...
*/
路由器.post('/', (ctx) => {
Const data = CTX.请求.身体;
Const 错误 = {};
if (!字符串(数据.名称).削减()){
错误.name =['需要输入姓名'];
}
if (!(/ ^ \ 0-9a-za-z \.+ _ \] + @ [\ 0-9a-za-z \.\+_]+\.[a-zA-Z]{2,} /美元).测试(String(数据.电子邮件))){
错误.email = [' email无效.'];
}
如果(对象.键(错误).长度){
返回ctx.错误(400年,{错误});
}
const 用户 =等待用户.创建({
名称:数据.名字
电子邮件:数据.电子邮件,
});
ctx.Body = 用户.toJSON ();
});
现在让我们重写这段代码,并使用datalize验证这个请求:
Const datalize = require('datalize');
Const field = datalize.场;
/**
* @api {post} /创建用户
* ...
*/
路由器.邮报》(' / ',datalize ([
字段(名字).削减().需要(),
字段(电子邮件).要求().电子邮件(),
]), (ctx) => {
if (!ctx.form.isValid) {
返回ctx.错误(400,{错误:CTX.form.错误});
}
const 用户 =等待用户.创建(ctx.形式);
ctx.Body = 用户.toJSON ();
});
更短,更简洁,便于阅读. 使用datalize,您可以指定字段列表并链接到尽可能多的字段 规则 (如果输入无效则抛出错误的函数)或 过滤器 (格式化输入的函数).
规则和过滤器按照定义的顺序执行, 所以如果你想要先为一个字符串删除空白,然后检查它是否有任何值, 你必须定义 .削减()
之前 .要求()
.
然后Datalize将创建一个对象(可用) .form
(在更广泛的上下文对象中),仅使用您指定的字段, 所以你不需要再列一遍. 的 .form.isValid
属性告诉您验证是否成功.
如果我们不想对每个请求都检查表单是否有效, 我们可以添加一个全局中间件,如果数据没有通过验证,它就会取消请求.
要做到这一点,我们只需将这段代码添加到 引导文件 我们在哪里创建Koa/Express应用实例.
Const datalize = require('datalize');
//设置datalize在验证失败时抛出错误
datalize.设置(“autoVali日期”,真正的);
//只有Koa
//添加到Koa中间件链的最开始
应用程序.use(异步 (ctx, next) => {
尝试{
等待下一个();
} catch (err) {
If (err).错误){
ctx.状态= 400;
ctx.Body = err.toJSON ();
} else {
ctx.状态= 500;
ctx.body = '内部服务器错误';
}
}
});
//只有Express
//添加到Express中间件链的最末端
应用程序.使用(函数(err, req, res, next) {
If (err).错误){
res.状态(400).发送(犯错.toJSON ());
} else {
res.发送(500).send('内部服务器错误');
}
});
我们不需要再检查data是否有效,因为datalize会帮我们检查. 如果数据无效,它将返回一条带有无效字段列表的格式化错误消息.
是的,您甚至可以非常容易地验证您的查询参数—它不必与 帖子
只请求. 我们只需使用 .查询()
方法,唯一的区别是数据存储在 .data
对象而不是 .form
.
Const datalize = require('datalize');
Const field = datalize.场;
/**
* @api {get} /列出用户
* ...
*/
路由器.邮报》(' / ',datalize.查询([
字段(关键字).削减(),
字段(页面).默认的(1).数量(),
字段(“perPage”).要求().Select ([10,30,50]),
]), (ctx) => {
Const limit = CTX.data.perPage;
Const 在哪里 = {
};
如果(ctx.data.关键字){
在哪里.name ={{操作步骤.如:ctx.data.关键词+ '%'};
}
const 用户s =等待用户.findAll ({
在那里,
限制,
抵消:(ctx.data.页- 1)*限制;
});
ctx.Body = 用户s;
});
还有一个辅助方法用于参数验证, .参数()
. 通过在路由器中传递两个数据中间件,可以同时验证查询和表单数据 .post ()
方法.
到目前为止,我们在节点中使用了非常简单的数据.Js表单验证. 现在让我们尝试一些更复杂的字段,如数组、嵌套对象等.:
Const datalize = require('datalize');
Const field = datalize.场;
const DOMAIN_ERROR = "Email的域名在其DNS记录中没有有效的MX(邮件)条目";
/**
* @api {post} /创建用户
* ...
*/
路由器.邮报》(' / ',datalize ([
字段(名字).削减().需要(),
字段(电子邮件).要求().电子邮件().custom((value) => {
return new Promise((resolve, reject) => {
dns.解决(值.split('@')[1], 'MX', 函数(err, 地址) {
如果(err b| b| !地址| | !地址.长度){
return reject(new Error(DOMAIN_ERROR));
}
解决();
});
});
}),
字段(类型).要求().选择((“管理”、“用户”)),
字段(语言).数组().容器([
字段(id).要求().id(),
字段(水平).要求().Select(['初学者','中级','高级'])
]),
字段(“集团”).数组().id(),
]), 异步 (ctx) => {
Const {languages, groups} = CTX.形式;
删除ctx.form.语言;
删除ctx.form.组织;
const 用户 =等待用户.创建(ctx.形式);
等待UserGroup.bulkCreate(集团.map(groupId => ({
groupId,
用户名:用户.id,
})));
等待UserLanguage.bulkCreate(语言.map(item => ({
languageId:条目.id,
用户名:用户.id,
水平:项.的水平,
));
});
如果没有数据的内置规则,我们需要进行验证, 控件创建自定义数据验证规则 .自定义()
方法(很好的名字,对吧?),并在那里编写必要的逻辑. 对于嵌套对象,有 .容器()
方法中指定字段列表的方式与 datalize ()
函数. 可以在容器中嵌套容器,也可以使用 .数组()
过滤器,它将值转换为数组. 当 .数组()
过滤器不使用容器, 指定的规则或筛选器应用于数组中的每个值.
So .数组().select()(“读”、“写”)
会检查数组中的每个值是否都是 “读”
or “写”
如果没有,它将返回一个包含所有有错误的索引的列表. 很酷,是吧??
把
/补丁
当涉及到更新数据时 把
/补丁
(or 帖子
),您不必重写所有的逻辑、规则和过滤器. 你只需要添加一个额外的过滤器,比如 .可选的()
or .补丁()
,它将从上下文对象中删除未在请求中定义的任何字段. (.可选的()
将使它始终是可选的,然而 .补丁()
只有当HTTP请求的方法是 补丁
.)您可以添加这个额外的过滤器,这样它就可以在数据库中创建和更新数据.
Const datalize = require('datalize');
Const field = datalize.场;
const 用户Validator = datalize([]
字段(名字).补丁().削减().需要(),
字段(电子邮件).补丁().要求().电子邮件(),
字段(类型).补丁().要求().选择((“管理”、“用户”)),
]);
const 用户EditMiddleware = 异步 (ctx, next) => {
const 用户 =等待用户.findByPk (ctx.参数个数.id);
//如果没有找到用户,取消请求
if (!用户){
抛出新的错误('用户未找到.');
}
//在请求中存储用户实例,以便稍后使用
ctx.User = User;
返回下一个();
};
/**
* @api {post} /创建用户
* ...
*/
路由器.post('/', 用户Validator, 异步 (ctx) => {
const 用户 =等待用户.创建(ctx.形式);
ctx.Body = 用户.toJSON ();
});
/**
* @api {put} /更新用户
* ...
*/
路由器.put('/:id', 用户EditMiddleware, 用户Validator, 异步 (ctx) => {
等待ctx.用户.更新(ctx.形式);
ctx.Body = CTX.用户.toJSON ();
});
/**
* @api {patch} /给用户打补丁
* ...
*/
路由器.patch('/:id', 用户EditMiddleware, 用户Validator, 异步 (ctx) => {
if (!Object.键(ctx.形式).长度){
返回ctx.error(400, {message: 'Nothing to up日期 . '.'});
}
等待ctx.用户.更新(ctx.形式);
ctx.Body = CTX.用户.toJSON ();
});
使用两个简单的中间件,我们可以为所有中间件编写大部分逻辑 帖子
/把
/补丁
方法. 的 用户EditMiddleware ()
函数验证我们想要编辑的记录是否存在,否则抛出错误. 然后 用户Validator ()
是否对所有端点进行验证. 最后, .补丁()
过滤器将从 .form
如果请求的方法没有定义,则调用 补丁
.
在自定义过滤器中,您可以获取其他字段的值并基于该值执行验证. 您还可以从上下文对象获取任何数据, 比如请求或用户信息, 因为它都在自定义函数回调参数中提供.
该库包含一组基本的规则和过滤器, 但是您可以注册可用于任何字段的自定义全局过滤器, 所以你不必一遍又一遍地写同样的代码:
Const datalize = require('datalize');
const字段= datalize.场;
场.原型.日期 = 函数(format = 'YYYY-MM-DD') {
返回这.add(功能(价值){
Const 日期 = value ? Moment (value, format): null;
if (!日期| | !日期.isValid ()) {
抛出新的错误('% 5 '不是一个有效的日期.');
}
返回日期.格式(格式);
});
};
场.原型.日期Time = 函数(format = 'YYYY-MM-DD HH:mm') {
返回这.日期(格式);
};
有了这两个自定义过滤器,您可以将字段链接起来 .日期()
or .日期Time ()
用于验证日期输入的过滤器.
文件也可以使用datalize进行验证:有专门针对文件的过滤器,例如 .文件()
, .mime ()
, .尺寸()
因此,您不必单独处理文件.
我一直在为节点使用datalize.js表单验证已经在几个生产项目中使用,包括小型和大型api. 它帮助我按时交付优秀的项目,减轻了压力,同时使它们更具可读性和可维护性. 在一个项目中,我甚至用它来验证WebSocket消息的数据,方法是编写一个简单的Socket包装器.IO的用法和在Koa中定义路由差不多,这很好. 如果有足够的兴趣,我可能会写一个教程.
我希望本教程将帮助您和我在节点中构建更好的api.Js,具有完美验证的数据 没有安全问题 或者内部服务器错误. 最重要的是, 我希望它将为您节省大量的时间,否则您将不得不使用JavaScript编写额外的表单验证函数.
节点.Js是后端平台. 顾名思义,节点.js应用程序是用JavaScript (js)或任何可以编译或翻译成它的语言编写的.
cpu繁重的计算在节点上不能很好地工作.Js,因为这会阻塞它的事件循环.
用户可以将任何类型的数据发送到后端——也许它没有在前端进行验证, 或者直接通过你的API提交. 这些数据在后端主要以纯文本或二进制格式接收,必须对其进行解析和验证,以防止由不正确/恶意用户输入引起的错误.
Express是一个基于节点的web框架.js,而节点.js是在后端运行JavaScript的服务器环境.
节点.Js有很多用例,但它最适合用于实时应用.g. 聊天、数据流、协作服务)、服务器端呈现的web应用程序、cli和api.
以确保输入数据的格式和类型正确, 该数据只包含允许的值等.,它必须经过验证. 否则,在将其插入数据库时可能会遇到问题. 验证还可以防止恶意用户数据导致的安全泄漏.
世界级的文章,每周发一次.
世界级的文章,每周发一次.