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

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

问题描述

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

我有一个API,它使用在Node.js中编写的lambda函数在DynamoDB中编写内容.当我在AWS控制台中调用它时,API会按预期工作.我发送这样的尸体:

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

这将在我的dynamoDB表中创建条目.但是,当我用Postman调用相同的API(刚刚部署)时,出现此错误:

  {"statusCode":"400","body":一个或多个参数值无效:AttributeValue可能不包含空字符串",标题":{"Content-Type":"application/json"}} 

当我检查cloudwatch为何失败时,我看到:转换前的方法请求主体:[二进制数据]

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

  Content-Type:application/json接受:application/json 

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

  {用户身份":"",姓名":"",名":"",} 

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

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

提前谢谢!

我正在添加lambda代码函数

 'use strict';console.log('Function Prep');const doc = require('dynamodb-doc');const dynamo = new doc.DynamoDB();exports.handler =(事件,上下文,回调)=>{const done =(err,res)=>callback(null,{statusCode:错误?'400':'200',身体:嗯?err.message:res,标头:{'内容类型':'应用程序/json'},});开关(event.httpMethod){案例删除":dynamo.deleteItem(event.body,完成);休息;案例"HEAD":dynamo.getItem(event.body,完成);休息;案例"GET":如果(event.queryStringParameters!==未定义){dynamo.scan({TableName:event.queryStringParameters.TableName},完成);}别的 {dynamo.getItem(event.body,完成);}休息;案例"POST":dynamo.putItem(event.body,完成);休息;案例"PUT":dynamo.putItem(event.body,完成);休息;默认:完成(新错误(不支持的方法"$ {event.httpMethod}")));}}; 

解决方案

这是因为从AWS Lambda的控制台进行测试时,您正在发送实际期望的JSON.但是,当从API网关调用此事件时,该事件看起来就不同了.

您必须访问 event.body 对象才能获取JSON,但是,主体是Stringified JSON,这意味着您必须先对其进行解析.

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

JSON.parse(event.body).

如果您使用的是Python,则可以执行以下操作:

json.loads(event ["body"])

如果您使用任何其他语言,建议您查看如何从给定的String解析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",接受语言":"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":美国",主机":"wt6mne2s9k.execute-api.us-west-2.amazonaws.com",不安全升级请求":"1","User-Agent":"Mozilla/5.0(Macintosh; Intel Mac OS X 10_11_6)AppleWebKit/537.36(KHTML,例如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":你好"},"requestContext":{"accountId":"123456789012","resourceId":"us4z18","stage":测试","requestId":"41b45ea3-70b5-11e6-b7bd-69b5aaebc7d9",身份": {"cognitoIdentityPoolId":",帐户ID": "","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,例如Gecko)Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",用户":"},"resourcePath":"/{proxy +}","httpMethod":"GET","apiId":"wt6mne2s9k"},"resource":"/{proxy +}","httpMethod":"GET","queryStringParameters":{名称":我"},"stageVariables":{"stageVarName":"stageVarValue"},"body":'{\" user-id \:\" 123 \,\" name \:\"名称\,\"名字\:\"名字\}'"} 

编辑

在评论中进行进一步讨论之后,另一个问题是您正在使用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网关POST方法在测试过程中有效,但不适用于邮递员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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