API Gateway POST 方法在测试期间有效,但不适用于邮递员 [英] API Gateway POST method working during tests, but not with postman

查看:21
本文介绍了API Gateway POST 方法在测试期间有效,但不适用于邮递员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我会尽量清楚地解释我的问题.

我有一个 API,它使用 Node.js 编写的 lambda 函数在 DynamoDB 中编写一些东西.当我在 AWS 控制台中调用它时,API 按预期工作.我发送这样的正文:

<代码>{"user-id":"4dz545zd","name":"布什","firstname":"杰拉德",}

这会在我的 dynamoDB 表中创建条目.但是当我使用 Postman 调用相同的 API(新部署的)时,我收到此错误:

<代码>{"状态码": "400","body": "一个或多个参数值无效:一个 AttributeValue 不能包含空字符串",标题":{内容类型":应用程序/json"}}

当我检查 cloudwatch 失败的原因时,我看到:转换前的方法请求体:[Binary Data]

这很奇怪,因为我发送了带有两个标头的 JSON:

Content-Type:application/json接受:应用程序/json

然后在cloudwatch中,我看到正在处理的是:

<代码>{用户身份":"",姓名":"",名":"",}

这解释了错误,但我不明白为什么当我用邮递员发送它时,它不为空,使用 json 格式,它仍然将它作为二进制"数据发送,所以我的映射规则(因此 lambda 使用空的 json 处理它):

#set($inputRoot = $input.path('$')){"httpMethod": "POST",身体": {"表名": "用户",物品": {"user-id":"$inputRoot.get('user-id')","name":"$inputRoot.get('name')","firstname":"$inputRoot.get('firstname')",}}}

先谢谢你!

我正在添加 lambda 代码函数

'use strict';console.log('函数准备');const doc = require('dynamodb-doc');const dynamo = 新 doc.DynamoDB();export.handler = (事件、上下文、回调) =>{const done = (err, res) =>回调(空,{状态代码:错误?'400':'200',身体:错了?错误消息:res,标题:{'内容类型':'应用程序/json'},});开关(事件.httpMethod){案例删除":dynamo.deleteItem(event.body, done);休息;案例头":dynamo.getItem(event.body, done);休息;案例获取":if (event.queryStringParameters !== undefined) {dynamo.scan({ TableName: event.queryStringParameters.TableName }, done);}别的 {dynamo.getItem(event.body, done);}休息;案例'POST':dynamo.putItem(event.body, done);休息;案例'PUT':dynamo.putItem(event.body, done);休息;默认:done(new Error(`不支持的方法${event.httpMethod}"`));}};

解决方案

那是因为从 AWS Lambda 的控制台进行测试时,您发送的是您实际期望的 JSON.但是当它从 API Gateway 调用时,事件看起来不同.

您必须访问 event.body 对象才能获得 JSON,但是,主体是字符串化 JSON,这意味着您必须首先解析它.

您没有指定使用哪种语言进行编码,但如果您使用的是 NodeJS,则可以像这样解析正文:

JSON.parse(event.body).

如果您使用的是 Python,那么您可以这样做:

json.loads(event["body"])

如果您使用任何其他语言,我建议您查看如何从给定的字符串解析 JSON

这可以满足您的需求.

API Gateway 中的事件如下所示:

<代码>{"path": "/test/hello",标题":{"接受": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8","Accept-Encoding": "gzip, deflate, lzma, sdch, br","Accept-Language": "en-US,en;q=0.8","CloudFront-Forwarded-Proto": "https","CloudFront-Is-Desktop-Viewer": "true","CloudFront-Is-Mobile-Viewer": "false","CloudFront-Is-SmartTV-Viewer": "false","CloudFront-Is-Tablet-Viewer": "false","CloudFront-Viewer-Country": "美国","Host": "wt6mne2s9k.execute-api.us-west-2.amazonaws.com",升级不安全请求":1","用户代理": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48","Via": "1.1 fb7cca60f0ecd82ce07790c9c5eef16c.cloudfront.net (CloudFront)","X-Amz-Cf-Id": "nBsWBOrSHMgnaROZJK1wGCZ9PcRcSpq_oSXZNQwQ10OTZL4cimZo3g==","X-Forwarded-For": "192.168.100.1, 192.168.1.1","X-转发端口": "443",X 转发协议":https"},路径参数":{代理":你好"},请求上下文":{"accountId": "123456789012","resourceId": "us4z18",阶段":测试","requestId": "41b45ea3-70b5-11e6-b7bd-69b5aaebc7d9",身份": {"cognitoIdentityPoolId": "",帐户ID": "","cognitoIdentityId": "","来电者": "","apiKey": "","sourceIp": "192.168.100.1","cognitoAuthenticationType": "","cognitoAuthenticationProvider": "","userArn": "","userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",用户":"},"resourcePath": "/{proxy+}","httpMethod": "GET","apiId": "wt6mne2s9k"},"资源": "/{proxy+}","httpMethod": "GET",查询字符串参数":{姓名":我"},舞台变量":{"stageVarName": "stageVarValue"},"body": "'{\"user-id\":\"123\",\"name\":\"name\", \"firstname\":\"firstname\"}'"}

编辑

在评论中进一步讨论后,还有一个问题是您使用的是 DynamoDB API 而不是 DocumentClient API.使用 DynamoDB API 时,您必须指定对象的类型.另一方面,DocumentClient 将这种复杂性抽象化了.

我还稍微重构了您的代码(为了简单起见,目前仅处理 POST),因此您可以使用

这是我的邮递员请求:

使用正确的标题:

在创建 API 网关时,我选中了使用 Lambda 代理集成 框.我的 API 如下所示:

如果您重现这些步骤,它应该可以正常工作.

i will try to explain my problem clearly.

I have an API who writes something in DynamoDB with a lambda function written in Node.js. When i'm calling it within the AWS console, the API works as expected. I send a body like that:

{
        "user-id":"4dz545zd",
        "name":"Bush",
        "firstname":"Gerard",
}

And that creates the entry within my dynamoDB table. But when i call the same API (freshly deployed) with Postman, i get this error:

{
    "statusCode": "400",
    "body": "One or more parameter values were invalid: An AttributeValue may not contain an empty string",
    "headers": {
        "Content-Type": "application/json"
    }
}

When i check in cloudwatch why it fails, i see: Method request body before transformations: [Binary Data]

This is weird, because i sent JSON with the two headers:

Content-Type:application/json
Accept:application/json

And then in cloudwatch, i see that being processed is:

{
        "user-id":"",
        "name":"",
        "firstname":"",
}

Thats explains the error, but i don't understand why when i'm sending it with postman, being not empty, with the json format, it still sends it as "binary" data, and so not being processed by my mapping rule (And so lambda processing it with an empty json):

#set($inputRoot = $input.path('$'))
  {
  "httpMethod": "POST",
  "body": {
    "TableName": "user",
    "Item": { 
        "user-id":"$inputRoot.get('user-id')",
        "name":"$inputRoot.get('name')",
        "firstname":"$inputRoot.get('firstname')",
                }
            }
}

Thank you in advance !

EDIT: I'm adding the lambda code function

'use strict';

console.log('Function Prep');
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();

exports.handler = (event, context, callback) => {

    const done = (err, res) => callback(null, {
        statusCode: err ? '400' : '200',
        body: err ? err.message : res,
        headers: {
            'Content-Type': 'application/json'
        },
    });

    switch (event.httpMethod) {
        case 'DELETE':
            dynamo.deleteItem(event.body, done);
            break;
        case 'HEAD':
            dynamo.getItem(event.body, done);
            break;
        case 'GET':
            if (event.queryStringParameters !== undefined) {
                dynamo.scan({ TableName: event.queryStringParameters.TableName }, done);
            }
            else {
                dynamo.getItem(event.body, done);
            }
            break;
        case 'POST':
            dynamo.putItem(event.body, done);
            break;
        case 'PUT':
            dynamo.putItem(event.body, done);
            break;
        default:
            done(new Error(`Unsupported method "${event.httpMethod}"`));
    }
};

解决方案

That's because when testing from AWS Lambda's console, you're sending the JSON you actually expect. But when this is invoked from API Gateway, the event looks different.

You'll have to access the event.body object in order to get your JSON, however, the body is a Stringified JSON, meaning you'll have to first parse it.

You didn't specify what language you're coding in, but if you're using NodeJS you can parse the body like this:

JSON.parse(event.body).

If you're using Python, then you can do this:

json.loads(event["body"])

If you're using any other language, I suggest you look up how to parse a JSON from a given String

That gives what you need.

This is what an event from API Gateway looks like:

{
    "path": "/test/hello",
    "headers": {
      "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
      "Accept-Encoding": "gzip, deflate, lzma, sdch, br",
      "Accept-Language": "en-US,en;q=0.8",
      "CloudFront-Forwarded-Proto": "https",
      "CloudFront-Is-Desktop-Viewer": "true",
      "CloudFront-Is-Mobile-Viewer": "false",
      "CloudFront-Is-SmartTV-Viewer": "false",
      "CloudFront-Is-Tablet-Viewer": "false",
      "CloudFront-Viewer-Country": "US",
      "Host": "wt6mne2s9k.execute-api.us-west-2.amazonaws.com",
      "Upgrade-Insecure-Requests": "1",
      "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",
      "Via": "1.1 fb7cca60f0ecd82ce07790c9c5eef16c.cloudfront.net (CloudFront)",
      "X-Amz-Cf-Id": "nBsWBOrSHMgnaROZJK1wGCZ9PcRcSpq_oSXZNQwQ10OTZL4cimZo3g==",
      "X-Forwarded-For": "192.168.100.1, 192.168.1.1",
      "X-Forwarded-Port": "443",
      "X-Forwarded-Proto": "https"
    },
    "pathParameters": {
      "proxy": "hello"
    },
    "requestContext": {
      "accountId": "123456789012",
      "resourceId": "us4z18",
      "stage": "test",
      "requestId": "41b45ea3-70b5-11e6-b7bd-69b5aaebc7d9",
      "identity": {
        "cognitoIdentityPoolId": "",
        "accountId": "",
        "cognitoIdentityId": "",
        "caller": "",
        "apiKey": "",
        "sourceIp": "192.168.100.1",
        "cognitoAuthenticationType": "",
        "cognitoAuthenticationProvider": "",
        "userArn": "",
        "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",
        "user": ""
      },
      "resourcePath": "/{proxy+}",
      "httpMethod": "GET",
      "apiId": "wt6mne2s9k"
    },
    "resource": "/{proxy+}",
    "httpMethod": "GET",
    "queryStringParameters": {
      "name": "me"
    },
    "stageVariables": {
      "stageVarName": "stageVarValue"
    },
    "body": "'{\"user-id\":\"123\",\"name\":\"name\", \"firstname\":\"firstname\"}'"
  }

EDIT

After further discussion in the comments, one more problem is that the you're using the DynamoDB API rather than the DocumentClient API. When using the DynamoDB API, you must specify the types of your objects. DocumentClient, on the other hands, abstracts this complexity away.

I have also refactored your code a little bit (only dealing with POST at the moment for the sake of simplicity), so you can make use of async/await

'use strict';

console.log('Function Prep');
const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event) => {

    switch (event.httpMethod) {
        case 'POST':
            await dynamo.put({TableName: 'users', Item: JSON.parse(event.body)}).promise();
            break;
        default:
            throw new Error(`Unsupported method "${event.httpMethod}"`);
    }
    return {
        statusCode: 200,
        body: JSON.stringify({message: 'Success'})
    }
};

Here's the Item in DynamoDB:

And this is my Postman request:

With proper headers:

When creating API Gateway, I checked the box Use Lambda Proxy integration. My API looks like this:

If you reproduce these steps it should just work.

这篇关于API Gateway POST 方法在测试期间有效,但不适用于邮递员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆