65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2016 年 4 月 19 日

CPOL

4分钟阅读

viewsIcon

13548

本系列文章的第 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 编辑本文。

© . All rights reserved.