如何将在 Lambda 上使用 NodeJS 丰富的对象返回到 API POST 请求

Posted

技术标签:

【中文标题】如何将在 Lambda 上使用 NodeJS 丰富的对象返回到 API POST 请求【英文标题】:How can I return an object enriched using NodeJS on Lambda to an API POST request 【发布时间】:2019-01-26 00:06:21 【问题描述】:

我正在使用 Lambda/NodeJS 在 AWS 上进行开发。为了响应 POST 请求,我试图返回一个数据对象,我用预签名的 URL 来丰富它以访问 S3 上的图像。问题是返回数据,因此它实际上是作为 API 的响应返回的。我知道这是一个异步问题,但到目前为止还没有解决它。非常感谢任何帮助。

我有以下代码:


    
        docClient.query(dbparams, function (err, data) 
            if (err) 
                console.log(err);
                _response = buildOutput(500, err);
                return callback(_response, null);
             else 
                dataObj = data.Items;
                var createOutput = function (callback) 
                    async.forEach(dataObj, function (item, callback) 
                        setObjectUrl(item, function (err, data) 
                            if (err) 
                                console.log(err);
                            
                            return callback(undefined, data);
                        );
                    );
                ;

                createOutput(function (err, data) 
                    if (err) 
                        console.log(err);
                    
                );
                _response = buildOutput(200, data);
                return callback(null, _response);
            
        );
    



function setObjectUrl(data, callback) 
    var s3params = 
        Bucket: s3Bucket,
        Key: 'avatars/' + data.username + '.jpg'
    ;

    S3.headObject(s3params, function (err, metadata) 
        if (err && err.code === 'NotFound') 
            //console.log('geen url voor deze gebruiker gevonden');
         else 
            data["url"] = S3.getSignedUrl('getObject', s3params);
        
        return callback(data);
    );


function buildOutput(statusCode, data) 
    let _response = 
        statusCode: statusCode,
        headers: 
            "Access-Control-Allow-Origin": "*"
        ,
        body: JSON.stringify(data)
    ;
    return _response;

【问题讨论】:

请格式化您的代码。 抱歉,刚刚做了。 不幸的是我不能分享数据,但我可以解释一下我得到的输出。我从从 docClient.query(dbparams, function (err, data) 回调的数据对象中得到响应。我想要的是来自 return callback(data); 在函数 setObjectUrl(data, callback) 中的数据. 【参考方案1】:

这就是我最终解决它的方法。感谢您的帮助。

const AWS = require('aws-sdk');
const util = require('util');

// Create reference to S3 client
var S3 = new AWS.S3(
    region: 'XXXXXXXXXXXX'
    apiversion: '2006-03-01'
);

// Lambda provided credentials
let creds = new AWS.EnvironmentCredentials('AWS'); 
const dynamoConfig = 
    credentials: creds,
    region: process.env.AWS_REGION
;

const dynamoDB = new AWS.DynamoDB.DocumentClient(dynamoConfig);
const dynamoDBGetAsync = util.promisify(dynamoDB.query).bind(dynamoDB);

// some variables we need
const ddbTable = 'database';
var _response;
var s3Bucket = 'photo-bucket';

module.exports.handler = async (event, context, callback) => 

    if (event.resource === '/' && event.httpMethod === "GET") 
        const username = event.queryStringParameters.userid;

        var params = 
            TableName: ddbTable,
            IndexName: 'userid-index',
            KeyConditionExpression: "#uid = :uid",
            ExpressionAttributeNames: 
                "#uid": "userid"
            ,
            ExpressionAttributeValues: 
                ":uid": username
            
        ;

        let result= await dynamoDBGetAsync(params);
        for (let item of result.Items) 
            if (item.username !== undefined ||  null) 
                item.url = await getObjectUrl(item);
            
        
        _response = buildOutput(200, result);
        return callback(null, _response);
    
;

function getObjectUrl(data) 
    console.log("Data: " +JSON.stringify(data));
    const s3params = 
        Bucket: s3Bucket,
        Key: 'avatars/' + data.username + '.jpg'
    ;

    return new Promise((resolve, reject) => 
        S3.headObject(s3params, function (err, metadata) 
            console.log('parameters: ' + JSON.stringify(s3params));
            if (err && err.code === 'NotFound') 
                console.log(JSON.stringify(err));
             else 
                const signedUrl = S3.getSignedUrl('getObject', s3params);
                console.log(signedUrl);
                resolve(signedUrl);
            
         );
     );


function buildOutput(statusCode, data) 
    let _response = 
        statusCode: statusCode,
        headers: 
            "Access-Control-Allow-Origin": "*"
        ,
        body: JSON.stringify(data)
    ;
    return _response;

【讨论】:

【参考方案2】:

所有异步调用完成后,您需要调用 lambda 的回调。有一些不同的选项可以修复您的代码,但我建议使用 Promise 和 async/await。这不仅可以使您的代码正常工作,还可以使其更具可读性并有助于避免回调地狱。

您需要将您的 S3 调用封装在 Promise 中:

function getObjectUrl(data) 
    const s3params = 
        Bucket: s3Bucket,
        Key: 'avatars/' + data.username + '.jpg'
    ;

    return new Promise((resolve, reject) => 
        S3.headObject(s3params, function (err, metadata) 
            if (err) 
                //handle error
             else 
                const signedUrl = S3.getSignedUrl('getObject', s3params);
                resolve(signedUrl);
            
         );
     );

之后你只需要循环调用你的promisified函数:

for(let item of data.Items) 
    item.url = await getObjectUrl(data);

别忘了让你的主函数异步并配置你的 lambda 以使用 NodeJS 8.10

【讨论】:

感谢您的及时回复。当我在带有 NodeJS 8.10 的 Lambda 中使用此代码时,出现解析错误:在等待之后立即出现意外令牌 getObjectUrl。我已经使我的主要功能异步。笔记。我一直在尝试使用 aync await 解决问题,但遇到了同样的问题……有什么建议吗? 这很奇怪,async/await 在 lambda 上与 NodeJS 8.10 完美配合。你能用你的异步 lambda 处理程序发布你的代码部分吗? Sasha,我认为问题在于我必须使 docClient.query 异步,而不是主要,因为这是 for 循环所在的位置。试图弄清楚如何使 docClient.query 异步... 这可能是不可能的,因为它具有固有的异步性质。 哦,我明白了。您想要做的不是使 docClient 异步,它已经是异步的。您实际需要做的是使该方法被承诺。在 nodejs 8 中有一种简单而干净的方法: const util = require('util');常量 fs = 要求('fs'); const stat = util.promisify(fs.stat);结果 = 等待 stat('.');

以上是关于如何将在 Lambda 上使用 NodeJS 丰富的对象返回到 API POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

使用 AWS Lambda (NodeJS) 创建 AWS AppSync

如何从 NodeJS 和 Lambda 在 EMR Hive 上运行查询

AWS Lambda - 下载文件,并在同一个函数中使用它 - nodejs

如何使用依赖项创建AWS nodejs lambda函数

如何使用 NodeJS 在本地测试 AWS Lambda 处理程序?

使用nodejs中的AWS Lambda函数上传音频文件