【问题标题】:Why does my AWS Lambda Function return "Invalid JSON" error?为什么我的 AWS Lambda 函数返回“无效 JSON”错误?
【发布时间】:2023-03-23 22:58:01
【问题描述】:

我有一个几天前写的 lambda 函数,它在测试时表现得非常好。今天去测试后(不更改任何代码),我收到以下错误:"Invalid lambda function output : Invalid JSON"

这里是函数代码(Node.js 10.x):

const AWS = require("aws-sdk");
const joi = require("@hapi/joi");

const Cognito = new AWS.CognitoIdentityServiceProvider();

exports.handler = async (event) => {
    // NOTE: Cognito expects Username to be the user's email

    // Vars
    const userPoolId = process.env.COGNITO_USER_POOL_ID;
    const {email : UNSAFE_EMAIL, language : UNSAFE_LANGUAGE = "en-US"} = event;

    // Normalize email and language
    const UNSAFE_TRIMMED_EMAIL = UNSAFE_EMAIL.trim();
    const UNSAFE_TRIMMED_LANGUAGE = UNSAFE_LANGUAGE.trim();

    // Validate UNSAFE_INPUTS
    const languageRegex = /^[a-z]{2}-[A-Z]{2}$/;

    const schema = joi.object().keys({
        email: joi.string().trim().email({minDomainSegments: 2}).required(),
        language: joi.string().trim().min(2).max(5).regex(languageRegex).required()
    });

    const validationResult = joi.validate({
        email: UNSAFE_TRIMMED_EMAIL,
        language: UNSAFE_TRIMMED_LANGUAGE
    }, schema);

    if(validationResult.error) {
        console.log(JSON.stringify(validationResult.error, null, 2));
        return {
            statusCode: 400,
            body: JSON.stringify({
                error: true,
                error_message: "Invalid"
            })
        }
    }

    // Validation successful, change variable names to reflect
    const VALIDATED_EMAIL = UNSAFE_TRIMMED_EMAIL;
    const VALIDATED_LANGUAGE = UNSAFE_TRIMMED_LANGUAGE;

    // Cognito params
    // Username is the user's email
    // email is also required in UserAttributes in order to send confirmation
    // DesiredDeliveryMediums is required to send confirmation
    const params = {
        UserPoolId: userPoolId,
        Username: VALIDATED_EMAIL,
        UserAttributes: [
            {
                Name: "email",
                Value: VALIDATED_EMAIL
            },
            {
                Name: "custom:language",
                Value: VALIDATED_LANGUAGE
            } 
        ],
        DesiredDeliveryMediums: ["EMAIL"]
    }

    // Attempt to create user in Cognito
    try {
        const authRes = await Cognito.adminCreateUser(params).promise();
        console.log("Success: ", JSON.stringify(authRes, null, 2));
        return {
            statusCode: 200,
            body: JSON.stringify({
                success: true
            })
        }
    } catch(err) {
        console.log("Error: ", JSON.stringify(err, null, 2));
        return {
            statusCode: 400,
            body: JSON.stringify({
                error: true,
                error_message: err.message
            })
        }
    }
};

运行测试时,我在传入格式错误的事件数据时收到了预期的错误消息,并且在尝试两次使用相同的电子邮件创建用户时收到了 Cognito 错误。同样,这是意料之中的。但是,当传入用户池中没有用户的有效电子邮件时,我得到以下回复(为便于阅读而格式化):

Response:
{
  "statusCode": 400,
  "body": {
    "error": true,
    "error_message": "Invalid lambda function output : Invalid JSON"
  }
}

查看此函数连接到的 Cognito 用户池,我看到一个用户已成功创建。然而,没有像几天前那样向该电子邮件地址发送电子邮件。

所有记录的信息都表明我有一个无效的 JSON 错误,根本没有 authRes 记录。当删除对 Cognito 的调用和相应的 console.log 调用时,try 块运行成功。所以问题在于对 Cognito 的调用。

但是为什么这段代码在几天前可以正常工作时今天却失败了?这是让我非常沮丧的部分。

【问题讨论】:

    标签: node.js json aws-lambda amazon-cognito


    【解决方案1】:

    我很长一段时间都遇到了类似的问题。我很困惑,来自 sam-application 的一个 lambda 启动时没有问题,而另一个却给出了这样的错误。 事实证明,您需要增加超时时间。默认超时为 3 秒。尝试将其增加到 10 秒或更长时间。我把它留了 20 秒的余量,因为 Lambda 在执行后仍然会死

    【讨论】:

    • 知道为什么可以解决它吗?我将它从 3 增加到 10,我希望这能解决我的问题。不过,如果您能进一步提示我,我想更好地理解这一点。
    【解决方案2】:

    对我来说,事实证明我的一位同事正在试验自定义授权,因为这是我们需要在应用程序中采取的下一步。他采用了一个与自定义授权无关的 Lambda,并将其添加到 Cognito 用户池 > 常规设置 > 触发器 > 预身份验证和身份验证后字段,以观察 lambda 在 CloudWatch 中被触发,但没有从用户池。

    显然,提供任何类型的前/后授权的 lambda 会导致从 Lambda 发回的无效响应,从而导致“无效 JSON”响应。

    具体来说,在开发者控制台中收到的响应是“400 Bad Request”,响应为:

    {
      "__type":"InvalidLambdaResponseException",
      "message":"Invalid lambda function output : Invalid JSON"
    }
    

    在我确认没有人更改内置的 Amplify 身份验证 lambda 后,这让我开始在 Cognito 中进行挖掘,这要感谢 the_new 的回答。

    【讨论】:

      【解决方案3】:

      问题根本不在于这个 lambda 函数。这是 AWS 和我用作 Cognito 用户池的自定义消息触发器的 lambda 函数的问题。以下是问题所在:

      根据 AWS 文档,提供给自定义消息触发器 lambda 的事件数据对于 adminCreateUser 函数调用采用以下形式:

      {
        "version": 1,
        "triggerSource": "CustomMessage_AdminCreateUser",
        "region": "<region>",
        "userPoolId": "<userPoolId>",
        "userName": "<userName>",
        "callerContext": {
            "awsSdk": "<calling aws sdk with version>",
            "clientId": "<apps client id>",
            ...
        },
        "request": {
            "userAttributes": {
                "phone_number_verified": false,
                "email_verified": true,
                 ...
            },
            "codeParameter": "####",
            "usernameParameter": "username"
        },
        "response": {
            "smsMessage": "<custom message to be sent in the message with code parameter and username parameter>"
            "emailMessage": "<custom message to be sent in the message with code parameter and username parameter>"
            "emailSubject": "<custom email subject>"
        }
      }
      

      并且预计从自定义消息触发器 lambda 返回的数据与事件的格式相同 - 仅更改了 response 对象。

      这就是我为 lambda 写的:

      const email_message = require("./email_message");
      
      exports.handler = async (event) => {
          // Vars
          const {codeParameter, usernameParameter} = event.request;
          console.log("Cognito Event: ", event);
      
          // Check that codeParameter equals "####" and usernameParameter equals "username"
          // This is to ensure that no compromised values are entered into the html
          if(!(codeParameter === "####" && usernameParameter === "username")) {
              return null;
          }
      
      
          const newRes = {
              smsMessage: `Welcome: confirmation code is ${codeParameter} and username is ${usernameParameter}`,
              emailMessage: email_message({codeParameter, usernameParameter}),
              emailSubject: "Welcome To Our Site"
          }
      
          return {...event, response: newRes};
      };
      

      这在几天前测试时有效,因为事件对象是上面的形式。发生的事情是 AWS 偷偷地将 codeParameterusernameParameter 字段的内容更改为以下内容:

      {
          ...
          "codeParameter": "{####}",
          "usernameParameter": "{username}",
          ...
      }
      

      因此 lambda 函数返回 null,因为这些字符串未通过验证 - 并且 null 不是有效的 JSON。

      所以临时解决办法是验证这些新字符串。然而,这引起了一些担忧。为什么 AWS 会突然更改事件对象而没有更新文档?其次,我应该如何验证这些字符串可以安全地注入客户的电子邮件地址?我知道我可以清理usernameParameter,但是codeParameter 怎么样,因为它很可能包含危险字符,例如&lt; &gt; &amp; ' ",因为它是由随机符号生成的密码?如果我自己生成密码,我可以确定它不会包含来自恶意行为者的数据,因此无需清理。但如果它来自 AWS,谁能说这些价值不会受到损害?因此,为什么我首先添加了在这些值已更改的情况下应该失败的验证步骤。这正是发生的事情。

      简而言之,我的所有代码都按预期运行。 AWS 在没有通知的情况下突然更改了他们的事件对象。

      【讨论】:

      • 嗨,我遇到了同样的问题,我已经发布了这个问题 - stackoverflow.com/questions/67160929/… 我想知道你是如何找到响应对象中的变化的。如何解决我的问题并确定具体问题?
      • AWS 官方文档仍然很糟糕。这是互联网上唯一有此信息的地方,感谢您为我们其他人敲打桌子!
      猜你喜欢
      • 2021-11-01
      • 2023-03-27
      • 2021-03-30
      • 2020-09-03
      • 2011-11-18
      • 1970-01-01
      • 2021-01-18
      • 2021-11-05
      • 2020-09-05
      相关资源
      最近更新 更多