skip to Main Content

When running a test event with AWS Lambda (accessed via AWS Console) I am able to pass in the following body and properly parse the data to a custom struct

Custom struct:

type ContactForm struct {
    Phone int `json:"phone"`
    Email string `json:"email"`
    Business string `json:"business"`
    Message string `json:"message"`
    Name string `json:"name"`
    File_data *string `json:"file_data"`
    File_name *string `json:"file_name"`
    Uri *string
}

Event JSON:

{
    "email": "email.com",
    "phone": 80077777,
    "business": "business",
    "message": "message",
    "name": "name"
}

When attempting to hit the lambda via a function URL using postman or an XHR request the body when parsed defaults to nil, 0, and empty string values showing that it is not able to read the json body. This is the entry point of the golang lambda that should pass in the object, but fails when done via function URL

func HandleRequest(ctx context.Context, contactForm ContactForm) {
    if contactForm.File_data != nil && contactForm.File_name != nil {
        uri := create_file(*contactForm.File_data, *contactForm.File_name)
        contactForm.Uri = &uri
    }

    fmt.Printf("%v", contactForm)

    body := create_email_body(contactForm)
    sendEmail(contactForm.Business, body)
}

Is there additional logic that needs to be done when parsing via an endpoint in comparison to using a Test Event?

2

Answers


  1. So depending on how you’re posting the data to the function e.g ApiGatewayProxyRequest or in your case LambdaFunctionURLRequest, the event might contain extra fields and metadata. I’d suggest you log the raw event, see https://docs.aws.amazon.com/lambda/latest/dg/golang-logging.html and base your struct off of what you see actually posted to the function. This was the case when I’ve run into circumstances like yours.

    Also, there is a predefined package you can use to define your events: "github.com/aws/aws-lambda-go/events". I think events.LambdaFunctionURLRequest is what you need to set in your handler. Then you can parse the JSON from the body of this payload. See https://pkg.go.dev/github.com/aws/aws-lambda-go/events#LambdaFunctionURLRequest.

        func HandleRequest(ctx context.Context, event events.LambdaFunctionURLRequest) {
    
        body := []byte(event.Body)
    
        var contact ContactForm
        json.Unmarshal(body, &contact)
    
        log.Printf("%v", contact.Email)
    }
    

    Here is the same method/logic from GoPlayground.

    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    type ContactForm struct {
        Phone     int     `json:"phone"`
        Email     string  `json:"email"`
        Business  string  `json:"business"`
        Message   string  `json:"message"`
        Name      string  `json:"name"`
        File_data *string `json:"file_data"`
        File_name *string `json:"file_name"`
        Uri       *string
    }
    
    func main() {
        input := []byte(`{"email": "email.com","phone": 80077777,"business": "programming","message": "hello world","name": "John Doe"}`)
    
        var contact ContactForm
        json.Unmarshal(input, &contact)
    
        fmt.Printf("%v", contact.Email)
    }
    

    You’ll need to do some validation of the data to make sure it’s actually json or catch the cases in which the payload is bad. This is good programming in general, so I’m assuming you’d additionally deal with edge cases in which the body didn’t serialize into the struct.

    Login or Signup to reply.
  2. When the Lambda function is invoked through the function HTTP URL you receive the payload in the following format:

     {
       "version": "2.0",
       "routeKey": "$default",
       "rawPath": "/my/path",
       "rawQueryString": "parameter1=value1&parameter1=value2&parameter2=value",
       "cookies": [
         "cookie1",
         "cookie2"
       ],
       "headers": {
         "header1": "value1",
         "header2": "value1,value2"
       },
       "queryStringParameters": {
         "parameter1": "value1,value2",
         "parameter2": "value"
       },
       "requestContext": {
         "accountId": "123456789012",
         "apiId": "<urlid>",
         "authentication": null,
         "authorizer": {
             "iam": {
                     "accessKey": "AKIA...",
                     "accountId": "111122223333",
                     "callerId": "AIDA...",
                     "cognitoIdentity": null,
                     "principalOrgId": null,
                     "userArn": "arn:aws:iam::111122223333:user/example-user",
                     "userId": "AIDA..."
             }
         },
         "domainName": "<url-id>.lambda-url.us-west-2.on.aws",
         "domainPrefix": "<url-id>",
         "http": {
           "method": "POST",
           "path": "/my/path",
           "protocol": "HTTP/1.1",
           "sourceIp": "123.123.123.123",
           "userAgent": "agent"
         },
         "requestId": "id",
         "routeKey": "$default",
         "stage": "$default",
         "time": "12/Mar/2020:19:03:58 +0000",
         "timeEpoch": 1583348638390
       },
       "body": "Hello from client!",
       "pathParameters": null,
       "isBase64Encoded": false,
       "stageVariables": null
     }
    

    You can find more information about the format here.

    So, the second parameter of the HandleRequest should be a struct with that format. Your ContactForm is actually the body of the request. Be aware that body may be a string, so you’d need to parse it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search