简化和保护您的 Node.js AWS Lambda 代码 - 第 2 部分





5.00/5 (1投票)
本系列文章的第 2 部分,介绍如何使用 Node.js 简化和保护 AWS Lambda 代码
三部分系列文章的第二部分
如果您还没有看过 第 1 部分,请务必查看。
在本系列文章的第 1 部分中,我们学习了如何使用开源 npm 模块 vandium,为 Node.js Amazon Web Services (AWS) Lambda 快速添加验证和防止注入攻击的功能。
我们用 Vandium 包装的 Lambda 处理程序
以下代码是本系列文章第 1 部分的最终代码。第 1 部分以用 vandium 包装 AWS Lambda 处理程序结束。该处理程序本身由一系列嵌套的异步回调函数组成,用于将消息从一个用户发送到另一个用户。
'use strict';
const vandium = require( 'vandium' );
// our token validation service
const tokenValidator = require( './lib/token-validator' );
// our nosql datastore
const db = require( './lib/db' );
// message service
const msgService = require( './lib/messages' );
vandium.validation( {
userId: vandium.types.uuid().required(),
message: vandium.types.string().min( 1 ).max( 255 ).required()
});
exports.handler = vandium( function( event, context, callback ) {
tokenValidator.validateToken( event.token, function( err, result ) {
if( err ) {
return callback( err );
}
// sender id
let senderId = result.userId;
db.userExists( event.userId, function( err, result ) {
if( err ) {
return callback( err );
}
msgService.send( senderId, event.userId, event.message, function( err, result ) {
if( err ) {
return callback( err );
}
callback( null, 'sent' );
});
});
});
});
尽管这段代码功能齐全,但如果我们将来添加新功能,它可能不容易维护。可能出现问题的原因是,代码的结构开始类似于 Node.js 世界中已知的“回调地狱”。为了避免这种模式,JavaScript 语言引入了 **Promises**。
Promises
使用 Promises 可以消除回调地狱,同时提高代码的可维护性和可测试性。
让我们看看传统的 Node.js 回调模式
doThisFirst( 'start', function( err, result ) {
if( err ) {
console.log( err );
return;
}
doThisSecond( result.two, function( err, result ) {
if( err ) {
console.log( err );
return;
}
doThisThird( result.three, function( err, result ) {
if( err ) {
console.log( err );
return;
}
console.log( 'result:', result );
});
});
});
上面示例中的回调模式现在将被 Promises 替换,以同时减少代码量并提高代码的可读性。由于我们正在修改现有代码,并且不想更改我们的库代码,因此我们可以使用优秀的 bluebird 库来“Promisify”我们的代码。
来自上一示例的“promisified”代码现在看起来是这样的
const Promise = require( 'bluebird' );
// need to "promisify" our callbacks
const doThisFirstAsync = Promise.promisify( doThisFirst );
const doThisSecondAsync = Promise.promisify( doThisSecond );
const doThisThridAsync = Promise.promisify( doThisThird );
doThisFirstAsync( 'start' )
.then( function( result ) {
return doThisSecondAsync( result.two );
})
.then( function( result ) {
return doThisThirdAsync( result.three );
})
.then( function( result ) {
console.log( 'result:', result );
})
.catch( function( err ) {
console.log( err );
});
现在,代码不仅更少了,而且更容易理解。您可能注意到的一个细节是函数上的“Async
”后缀。这些是 bluebird 为我们创建的现有代码的 Promise 友好版本。
Lambda 和 Promises
现在我们知道了 Promises 是什么,让我们修改我们的 Lambda 处理程序。
'use strict';
const vandium = require( 'vandium' );
const bluebird = require( 'bluebird' );
// our token validation service - promisified
const tokenValidator = bluebird.promisifyAll( require( './lib/token-validator' ) );
// our nosql datastore - promisified
const db = bluebird.promisifyAll( require( './lib/db' ) );
// message service - promisified
const msgService = bluebird.promisifyAll( require( './lib/messages' ) );
vandium.validation( {
userId: vandium.types.uuid().required(),
message: vandium.types.string().min( 1 ).max( 255 ).required()
});
exports.handler = vandium( function( event, context, callback ) {
tokenValidator.validateTokenAsync( event.token )
.then( function( result ) {
let senderId = result.userId;
return db.userExistsAsync( event.userId );
.then( function() {
return msgService.sendAsync( senderId, event.userId, event.message );
});
})
.then( function( result ) {
callback( null, 'ok' );
})
.catch( function( err ) {
callback( err );
});
});
与之前的 Promise 示例一样,我们的代码更加扁平化,更容易理解,而且代码量更少。请注意,我们在初始“tokenValidator
”Promise 之后有一个嵌套的 Promise。我们这样做是因为在确定用户是否存在后,我们需要访问“senderId
”。在嵌套 Promises 时,内部 Promise 实例的返回值将被路由到外部实例的下一个“then
”。
但有一点感觉不太对——在 Lambda 处理程序的末尾使用回调函数来指示执行成功或失败。理想情况下,Lambda 处理程序应该支持 Promise 模式,并允许我们在处理程序结束时简单地返回值,而不是使用繁琐的回调函数。不幸的是,Lambda 处理程序不支持 Promises,但 vandium 支持。
Promises 作为一等公民
Vandium 的重点是减少需要维护的代码量,同时提供健壮性、安全性和功能性。在使用 vandium 时,Promises 被视为一等公民。要使用此功能,只需返回 promise,vandium 会处理其余的事情。
当 vandium 包装处理程序时,它会自动将成功和失败的 Promises 路由到回调处理程序。这一点很重要,因为它消除了开发人员为每种成功或失败情况手动将值路由到 Lambda 回调函数的需要。自然,这允许开发人员更多地专注于编写需要在 Lambda 处理程序中执行的实际逻辑。
以下是我们的“Promisified” Lambda 处理程序,已由 vandium 包装,返回值(或错误)会自动路由到 Lambda **回调**。
'use strict';
const vandium = require( 'vandium' );
const bluebird = require( 'bluebird' );
// our token validation service - promisified
const tokenValidator = bluebird.promisifyAll( require( './lib/token-validator' ) );
// our nosql datastore - promisified
const db = bluebird.promisifyAll( require( './lib/db' ) );
// message service - promisified
const msgService = bluebird.promisifyAll( require( './lib/messages' ) );
vandium.validation( {
userId: vandium.types.uuid().required(),
message: vandium.types.string().min( 1 ).max( 255 ).required()
});
exports.handler = vandium( function( event ) {
return tokenValidator.validateTokenAsync( event.token )
.then( function( result ) {
let senderId = result.userId;
return db.userExistsAsync( event.userId );
.then( function() {
return msgService.sendAsync( senderId, event.userId, event.message );
});
})
.then( function() {
return 'ok';
});
});
请注意,我们现在将 Promise 本身 **返回** 给 vandium 包装器。Vandium 将负责确定 Promise 的结果。在成功完成的情况下,或在发生错误的情况下,vandium 会自动将任一结果的值路由回 Lambda 处理程序的 callback 函数。此外,通过删除 context 和 callback 参数,我们的代码得到了进一步简化,因为它们不再需要了。
我们从 51 行嵌套的回调代码开始,现在剩下不到 40 行代码,并且流程大大简化。更少的代码行和更简单的分支结构可以轻松维护和测试。
第 2 部分总结
在 Lambda 处理程序中使用 Promises 可以减少我们需要维护和测试的复杂性和代码量。尽管 AWS Lambda 的 Node.js 实现很棒,但它缺乏对 Promises 的易用性支持,导致编写 Lambda 处理程序不必要地困难,并且导致处理程序的实际代码不优雅。Vandium 将 Promises 视为一等公民,使其比以往任何时候都更容易与 Lambda 处理程序一起使用。
未完待续,第 3 部分
有关 vandium 的更多信息,请访问项目 网站。
特别感谢 @mikey_s_e 编辑本文。