NodeJS AWS Lambda函数返回403 ERROR向dynamodb发送扫描命令时无法满足请求

4szc88ey  于 2023-03-07  发布在  Node.js
关注(0)|答案(2)|浏览(98)

我尝试通过API Gateway和用Javascript(ES6格式)编写的Lambda函数向DynamoDB发送扫描命令。每次我这样做时,我都会收到403错误:无法满足请求。我已将aws-sdk依赖项放入Lambda层。
Lambda函数:

import { DynamoDBClient, ScanCommand } from "@aws-sdk/client-dynamodb";
import { unmarshall } from '@aws-sdk/util-dynamodb';
const REGION = process.env.AWS_REGION;
const dynamo = new DynamoDBClient({region: REGION});
const tableName = process.env.MOVIE_TABLE;
//get table name from MOVIE_TABLE environment variable

/**
 *
 * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
 * @param {Object} event - API Gateway Lambda Proxy Input Format
 *
 * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html 
 * @param {Object} context
 *
 * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
 * @returns {Object} object - API Gateway Lambda Proxy Output Format
 * 
 */

//test with command: sam local start-api --port 8080 --log-file logfile.txt
export const lambdaHandler = async (event, context) => {
    let respBody;
    let sCode = 200;
    if (event.httpMethod !== 'GET') {
        throw new Error(`GET method only accepts GET method, you tried: ${event.httpMethod}`);
    }
    //All log statements are written to CloudWatch
    console.info('received request for get:', event);
    console.info('received context:', context);
    try {
        const params = {
            TableName: tableName,
        };
        const command = new ScanCommand(params);
        console.info(`params Tablename: ${params.TableName}`);
        console.info(`Region: ${REGION}`);
        respBody = await dynamo.send(command);
        respBody = respBody.Items;
        respBody = respBody.map((i) => unmarshall(i));
    } catch (err) {
        sCode = err.statusCode;
        respBody = err.message;
        var stack = err.stack;
        //const { requestId, cfId, extendedRequestId } = err.$$metadata;
        console.info('Error stacktrace: \n');
        console.info(stack);
        //console.info('Error metdata: \n');
        //console.log({ requestId, cfId, extendedRequestId });
    } finally {
        respBody = JSON.stringify(respBody);
        console.info(`About to return status: ${sCode}, respBody: ${respBody}`);   
    }
    const response = {
        statusCode: sCode,
        body: respBody
    };
    return response;
};

template.yaml:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  movie-crud-app
  
Globals:
  Function:
    Timeout: 3

Resources:
  GetItemsFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: lambda-handlers/get-items/
      Handler: get-items.lambdaHandler
      Runtime: nodejs18.x
      Description: A simple function to get items
      Policies:
        #Give Create/Read/Update/Delete permissions to MovieTable
        - DynamoDBCrudPolicy:
            TableName: !Ref MovieTable
      Environment:
        Variables:
          #Make table name accessible as environment variable from function code during execution
          MOVIE_TABLE: !Ref MovieTable
      Architectures:
        - x86_64
      Layers:
        - !Ref DependenciesLayer
      Events:
        GetItems:
          Type: Api 
          Properties:
            Path: /items
            Method: get
            
  DependenciesLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
        LayerName: sam-app-dependencies
        Description: Dependencies for movie crud app (aws-sdk/client-dynamodb)
        ContentUri: dependencies/
        CompatibleRuntimes:
          - nodejs18.x
        LicenseInfo: 'MIT'
        RetentionPolicy: Retain
  
  MovieTable:
    Type: AWS::DynamoDB::Table
    Properties: 
      AttributeDefinitions: 
        - AttributeName: year
          AttributeType: N
        - AttributeName: title
          AttributeType: S
      KeySchema: 
        - AttributeName: year
          KeyType: HASH #Partition key
        - AttributeName: title
          KeyType: RANGE #Sort key
      ProvisionedThroughput: 
        ReadCapacityUnits: 10
        WriteCapacityUnits: 10
      TableName: "MovieTable"

我100%确定API网关正在达到Lambda函数。来自AWS Cloudwatch的日志:
ERROR调用错误
{“错误类型”:“错误”,“错误消息”:“没有有效的终结点提供程序可用。",“堆栈”:[“错误:没有可用的有效终结点提供程序。",
“位于/var/运行时/节点模块/@aws-sdk/中间件-serde/dist-cjs/序列化中间件.js:10:15”,
“位于/var/运行时/节点模块/@aws-sdk/库-动态数据库/dist-cjs/基本命令/动态数据库文档客户端命令.js:11:20”,
“位于/opt/节点js/节点模块/@aws-sdk/中间件记录器/dist-cjs/记录器中间件.js:5:28”,
“位于/var/运行时/节点模块/@aws-sdk/lib-dynamodb/dist-cjs/命令/扫描命令.js:28:28”,
“位于动态数据库文档客户端.send(/var/运行时/节点模块/@aws-sdk/smithy客户端/dist-cjs/客户端.js:20:20)",
“在运行时。lambdaHandler [作为处理程序](文件:///var/任务/获取项. mjs:60:29)",
运行时。handleOnceNonStreaming(文件:///变量/运行时/索引. mjs:1085:29)
]
}
如果我使用curl,我会收到内部服务器错误。
此应用程序通过AWS sam部署:sam deploy --guided
我曾尝试用一个使用boto 3的简单python脚本向我的dynamoDB表发送扫描命令,这似乎起作用了,所以我想可能是权限或角色有问题,我可能错过了。
Roles assigned to my lambda function:
获取项函数角色策略的JSON:

{
    "Statement": [
        {
            "Action": [
                "dynamodb:GetItem",
                "dynamodb:DeleteItem",
                "dynamodb:PutItem",
                "dynamodb:Scan",
                "dynamodb:Query",
                "dynamodb:UpdateItem",
                "dynamodb:BatchWriteItem",
                "dynamodb:BatchGetItem",
                "dynamodb:DescribeTable",
                "dynamodb:ConditionCheckItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:us-east-1:[mytablename]",
                "arn:aws:dynamodb:us-east-1:[mytablename]/index/*"
            ],
            "Effect": "Allow"
        }
    ]
}

Perhaps the issue may have something to do with the integration request being LAMBDA_PROXY?
2023年3月6日更新:显然,任何发送到空数据表的Get或Scan命令都会自动给予“No valid endpoint provider”错误。一旦我用模拟数据填充了表,我就不再收到该错误。但是,如果我尝试使用Postman调用API,我仍然会收到403 Forbidden错误。如果我使用curl或Web浏览器,该函数可以工作。我的下一步是修复Postman的问题。

a8jjtwal

a8jjtwal1#

假设模板正确地创建了资源,请尝试使用以下代码。

const AWS = require("aws-sdk");
const dynamoDB = new AWS.DynamoDB.DocumentClient({
    region: process.env.AWS_REGION,
});

let REGION = process.env.AWS_REGION;
//get table name from MOVIE_TABLE environment variable in template.yaml
const tableName = process.env.MOVIE_TABLE;

export const lambdaHandler = async (event, context) => {
    let body;
    let statusCode = 200;
    if (event.httpMethod !== 'GET') {
        throw new Error(`GET method only accepts GET method, you tried: ${event.httpMethod}`);
    }
    console.info('received request for get:', event);
    console.info('received context:', context);
    try {
        var params = {
            TableName: tableName,
        };
        console.info(`Tablename: ${tableName}`);
        console.info(`DYNAMO: ${dynamo}`);
        console.info(`Region: ${REGION}`);
        body = await dynamoDB.scan(params).promise();
        body = body.Item;
    } catch (err) {
        StatusCode = err.statusCode;
        body = err.message;
        stack = err.stack;
        console.info('Error stacktrace: \n');
        console.info(stack);
    } finally {
        body = JSON.stringify(body);
        console.info(`About to return status: ${StatusCode}, body: ${body}`);
    }
    return {
        'statusCode': StatusCode,
        'body': body,
    };
};
gtlvzcf8

gtlvzcf82#

显然,任何发送到空数据表的Get或Scan命令都会自动给予“No valid endpoint provider”错误。一旦我用模拟数据填充了表,我就不再收到该错误。我能够通过创建一个API键并将其用于Postman来解决Postman问题。

相关问题