3

It's different from - How to pass a params from POST to AWS Lambda from Amazon API Gateway. Because I am able to convert the params in the API Gateway, but not able to pass it or print it with Golang. However, the same API Gateway is working fine for Python.

Below are the logs of my API Gateway and AWS Lambda(Golang). I can see the POST params are converted to JSON successfully. Still, I cannot see it in the Lambda function logs.

API Gateway Logs

(442f74ed-39e5-4372-bf85-42bf814f802f) Extended Request Id: EIaYxxMF3lQ=
(442f74ed-39e5-4372-bf85-42bf814f802f) Method request path: {}

(442f74ed-39e5-4372-bf85-42bf814f802f) Method request query string:    {}

(442f74ed-39e5-4372-bf85-42bf814f802f) Method request headers: {Accept=*/*, Cache-Control=max-age=259200, X-Twilio-Signature=ZWg2v7xxxfnBlPyxE=, User-Agent=TwilioProxy/1.1, X-Forwarded-Proto=https, I-Twilio-Idempotency-Token=e5d1xxx221bc4, X-Forwarded-For=54.xxxx.227, Host=xxxxxxx.execute-api.us-east-1.amazonaws.com, X-Forwarded-Port=443, X-Amzn-Trace-Id=Root=1-5de67103-7994dbxxx0dbd872, Content-Type=application/x-www-form-urlencoded}}  

(442f74ed-39e5-4372-bf85-42bf814f802f) Method request body before transformations: ToCountry=US&ToState=UT&SmsMessageSid=SMed65axxx595c7938df&NumMedia=0&ToCity=&FromZip=&SmsSid=SMed65aa5xxccdd595c7938df&FromState=&SmsStatus=received&FromCity=&Body=Testing+again&FromCountry=IN&To=%2Bxxxx848&ToZip=&NumSegments=1&MessageSid=SMed65axxxd595c7938df&AccountSid=AC23a2cbxxx65a66d98&From=%2B9xxxx5590&ApiVersion=2010-04-01

(442f74ed-39e5-4372-bf85-42bf814f802f) Endpoint request URI: https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:6xxxxxxxx6:function:Twillio_connector_test/invocations

(442f74ed-39e5-4372-bf85-42bf814f802f) Endpoint request headers: {x-amzn-lambda-integration-tag=442f74ed-39e5-4372-bf85-42bf814f802f, Authorization=*****27aa7a, X-Amz-Date=20191203T142819Z, x-amzn-apigateway-api-id=xxxxx, X-Amz-Source-Arn=arn:aws:execute-api:us-east-1:69xxxx886:xxxxxxx/v1/POST/message, Accept=application/x-www-form-urlencoded, User-Agent=AmazonAPIGateway_f7504e7yc6, X-Amz-Security-Token=IQoJbxxxhQH [TRUNCATED]

(442f74ed-39e5-4372-bf85-42bf814f802f) Endpoint request body after transformations: 
    {
        "ToCountry": "US",
        "ToState": "UT",
        "SmsMessageSid": "SMed65aaxxxxxx5c7938df",
        "NumMedia": "0",
        "ToCity": "",
        "FromZip": "",
        "SmsSid": "SMed65aaxxxxxx938df",
        "FromState": "",
        "SmsStatus": "received",
        "FromCity": "",
        "Body": "Testing+again",
        "FromCountry": "IN",
        "To": "%2B1xxxxxx848",
        "ToZip": "",
        "NumSegments": "1",
        "MessageSid": "SMed65aa5dxxxx7938df",
        "AccountSid": "AC23xxxd98",
        "From": "%2B9xxxxxx90",
        "ApiVersion": "2010-04-01"
    }

(442f74ed-39e5-4372-bf85-42bf814f802f) Endpoint response headers: {Date=Tue, 03 Dec 2019 14:28:20 GMT, Content-Type=application/json, Content-Length=43, Connection=keep-alive, x-amzn-RequestId=168394b7-c152-4434-af02-03a03b6f3090, x-amzn-Remapped-Content-Length=0, X-Amz-Executed-Version=$LATEST, X-Amzn-Trace-Id=root=1-5de67103-7994dbxxxxbe30dbd872;sampled=0}

(442f74ed-39e5-4372-bf85-42bf814f802f) Endpoint response body before transformations: "Lambda function is completed successfully"

(442f74ed-39e5-4372-bf85-42bf814f802f) Method response body after transformations: Lambda function is completed successfully

(442f74ed-39e5-4372-bf85-42bf814f802f) Method response headers: {X-Amzn-Trace-Id=Root=1-5de67103-7994dbxxxxxxd872;Sampled=0, Content-Type=application/xml}

Lambda function Code

func Handler(request events.APIGatewayProxyRequest) (string, error) {

    fmt.Println("request:", events.APIGatewayProxyRequestContext)
    fmt.Println("request:", events.APIGatewayProxyResponse)
    fmt.Println("request:", events.APIGatewayRequestIdentity)

    fmt.Printf("%+v\n", request)
    fmt.Println("request Body:", request.Body)
    fmt.Println("request HTTPMethod:", request.HTTPMethod)
    fmt.Println("request Headers:", request.Headers)
    fmt.Println("request:", request.RequestContext.RequestID)

    fmt.Println("PrettyPrint")
    prettyPrint(request)

Lambda function logs

request: {   map[] map[] map[] map[] map[] map[] {    {           }  map[]  } Good Day false}
{Resource: Path: HTTPMethod: Headers:map[] MultiValueHeaders:map[] QueryStringParameters:map[] MultiValueQueryStringParameters:map[] PathParameters:map[] StageVariables:map[] RequestContext:{AccountID: ResourceID: Stage: RequestID: Identity:{CognitoIdentityPoolID: AccountID: CognitoIdentityID: Caller: APIKey: AccessKey: SourceIP: CognitoAuthenticationType: CognitoAuthenticationProvider: UserArn: UserAgent: User:} ResourcePath: Authorizer:map[] HTTPMethod: APIID:} Body:Good Day IsBase64Encoded:false}
request Body: Good Day
request HTTPMethod: 
request Headers: map[]
request: 

I have tried many ways. But I can't get the required parameters in the Golang code. Please correct me if I am doing anything wrong. Any help will be appreciated.

PS: I have used this document as the reference to set up the API Gateway - https://www.twilio.com/docs/sms/tutorials/how-to-receive-and-reply-python-amazon-lambda. The setup worked fine for Python Lambda function.

Rahul Satal
  • 2,107
  • 3
  • 32
  • 53
  • I think your function signature for `Handler` is wrong, It should be two params, `Context` and `APIGatewayProxyRequest`. Try that, and it should help. Also, your return should be `(APIGatewayProxyResponse, error)` – Arun Poudel Dec 03 '19 at 18:23
  • @ArunPoudel Thanks for the comment. I have also tried with 'Context' param as below. Still, I didn't get the required parameters. func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (string, error) { – Rahul Satal Dec 04 '19 at 06:26
  • Can you also share your cloudformation template or how the API gateway is setup? – Arun Poudel Dec 04 '19 at 08:16
  • @ArunPoudel I have used this document as the reference to set up the API Gateway - https://www.twilio.com/docs/sms/tutorials/how-to-receive-and-reply-python-amazon-lambda. The set up also worked fine for the python Lambda function as stated in the document. – Rahul Satal Dec 04 '19 at 09:24
  • I noticed the request Body shows "Good Day". Where did this word born from? I guess it may inspire us? – Sky.Li Dec 04 '19 at 10:49
  • @Sky.Li Yup, you caught me. I have updated the body section while asking the question. Originally It was 'Testing again' which really sounds boring :) – Rahul Satal Dec 04 '19 at 10:56
  • @RahulSatal Aha...It's a really important clue. I think we don't need a "Good Day", but to "Testing Again"~ – Sky.Li Dec 04 '19 at 15:38

1 Answers1

1

Conclusion :

This problem is caused by the different param struct of lambda handler in golang and python.

Details:

  • The JSON made by API Gateway mapping template:
    {
        "ToCountry": "US",
        "ToState": "UT",
        "SmsMessageSid": "SMed65aaxxxxxx5c7938df",
        "NumMedia": "0",
        "ToCity": "",
        "FromZip": "",
        "SmsSid": "SMed65aaxxxxxx938df",
        "FromState": "",
        "SmsStatus": "received",
        "FromCity": "",
        "Body": "Testing+again",
        "FromCountry": "IN",
        "To": "%2B1xxxxxx848",
        "ToZip": "",
        "NumSegments": "1",
        "MessageSid": "SMed65aa5dxxxx7938df",
        "AccountSid": "AC23xxxd98",
        "From": "%2B9xxxxxx90",
        "ApiVersion": "2010-04-01"
    }
  • The event parameter in python:
    def lambda_handler(event, context):
        print("Received event: " + str(event))
        return '<?xml version=\"1.0\" encoding=\"UTF-8\"?>'\
           '<Response><Message>Hello world! -Lambda</Message></Response>'

Reference to AWS Python Doc:

event – AWS Lambda uses this parameter to pass in event data to the handler. This parameter is usually of the Python dict type. It can also be a list, str, int, float, or NoneType type.

  • [IMPORTANT PART] The events.APIGatewayProxyRequest parameter in golang:
// APIGatewayProxyRequest contains data coming from the API Gateway proxy
type APIGatewayProxyRequest struct {
    Resource                        string                        `json:"resource"` // The resource path defined in API Gateway
    Path                            string                        `json:"path"`     // The url path for the caller
    HTTPMethod                      string                        `json:"httpMethod"`
    Headers                         map[string]string             `json:"headers"`
    MultiValueHeaders               map[string][]string           `json:"multiValueHeaders"`
    QueryStringParameters           map[string]string             `json:"queryStringParameters"`
    MultiValueQueryStringParameters map[string][]string           `json:"multiValueQueryStringParameters"`
    PathParameters                  map[string]string             `json:"pathParameters"`
    StageVariables                  map[string]string             `json:"stageVariables"`
    RequestContext                  APIGatewayProxyRequestContext `json:"requestContext"`
    Body                            string                        `json:"body"`
    IsBase64Encoded                 bool                          `json:"isBase64Encoded,omitempty"`
}

So you can see, the json map you provide to Golang AWS lambda handler only match one key Body. That's the reason why the printf only prints the Testing Again.

Solution:

To get the correct result with lambda write by golang, I think you can refer to this part of AWS DOC. (It's really helpful for us to understand how the AWS API Gateway mapping template work.)

Rewrite the mapping template to match the APIGatewayProxyRequest JSON tag.

Rahul Satal
  • 2,107
  • 3
  • 32
  • 53
Sky.Li
  • 497
  • 1
  • 6
  • 16
  • Thanks @Sky.Li, for pointing out the cause of the issue and for the great explanation. I didn't find the matching template for `APIGatewayProxyRequest`. So, I have passed the entire request body to the Lambda using `Lambda proxy integration` and handled it in the lambda itself. – Rahul Satal Dec 05 '19 at 12:20
  • Not sure why one the AWS {developers,SAM libraries} couldn't abstract this insanity away and just leave us with the standard (w http.ResponseWriter, r *http.Request) interface! – hendry Sep 13 '20 at 01:00