I have the following architecture:
(1) A front-end form, which has a user input of file:
var data = new FormData();
data.append('username', 'USER123');
data.append('file', selectedFile); //selectedFile is a file which I capture form a form (user-submitted)
await fetch('myURL/test', {
method: 'post',
headers: {
"Access-Control-Allow-Origin": "*"
},
body:data
}).then(res => res.json()).then(json => console.log(json))
console.log('Done')
(2) An AWS API Gateway (which has a POST route of 'myURL/test'),
(3) An AWS Lambda Integration with the following Python Code:
def lambda_handler(event, context): #Lambda functions works and returns a response of "Hello from Lambda" during a POST request
# TODO implement
print(event['body']) //prints some base64 string
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
What I am trying to achieve: User submits form (with .zip file) from my front-end, (2) Lambda function receives the file, (3) Lambda function checks whether file is correct size, type, (4) Lambda function uploads file to S3 Bucket. The thing is I am unable to receive the file in my lambda function. Printing of the event['body'] prints out some weird string that I'm unsure on how to process.
Any tips?
Verify that the Incoming Request at the Gateway is actually passing the POST body content (the file) to the Lambda. Likely you'll need to setup a mapping template along these lines...
{ "content": "$input.body" }
Here's a pretty good article showing the end-to-end setup to upload a file:
https://medium.com/swlh/upload-binary-files-to-s3-using-aws-api-gateway-with-aws-lambda-2b4ba8c70b8e
I have a lambda function that puts items in a dynamodb table. It works perfectly fine with test events.
import boto3
class DBupload:
def __init__(self):
client = boto3.resource('dynamodb')
self.table=client.Table('labels')
def Create_data(self,event):
response=self.table.put_item(
Item={
'instanceID' : event['id'],
'imageID' : event['imageid'],
'labels' : event['labels']
}
)
return{
'statusCode' : response['ResponseMetadata']['HTTPStatusCode'],
'body' : 'Record ' + event['id'] + ' added'
}
def lambda_handler(event, context):
if event:
instance = DBupload()
if event['tasktype'] == "create":
create_result = instance.Create_data(event['data'])
return create_result
else :
return {
'statusCode': '404',
'body': 'Not found'
}
I make a REST API gateway, create a resource, and a POST method, with proxy, and I enable IAM and API key, before deploying it. I then go to usage plans, and add the stage to the usage plan, then deploy once more for good measure.
When I send the exact same request now, through Postman, it throws an Internal Server Error. I can send it through the API Gateway testing function which bypasses auth, and it gives me the following error log
Lambda execution failed with status 200 due to customer function error: 'tasktype'.
Method completed with status: 502
So it looks like it is NOT an API error because the error log references the customer function in lambda. But the Lambda function works perfectly fine when I send my item in as a test-event through lambda. What gives?
Here is the test event btw
{
"tasktype":"create",
"data":{
"id":"testinsanceid",
"imageid":"testimageid",
"labels":{
"labeltype1":"labelname1",
"labeltype2":"labelname2"
}
}
}
Try
if 'tasktype' in event and event['tasktype'] == "create":
This will ensure you're not taking a dependency on something that doesn't exist.
Also print(event) will allow you to ensure the event object is what you expect.
I wrote a simple python test, that calls a lambda behind an API gateway in a loop.
for doc in docs:
payload = json.dumps({})
headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", f"https://XXXXXXXX.execute-api.eu-central-1.amazonaws.com/docs", headers=headers, data=payload)
I expect that the "Concurrent executions" monitor shows a constant value "1". But what I get is sometimes a "2":
Does anyone know why? Does AWS Cloudwatch only query the difference between "START" and "END" logs in a specific timeframe?
I'm trying to trigger Email using SES and API gateway without using Lambda function. It will contain pdf file and text. I'm able to trigger Email using QueryString parameters, but as i need to send files also I want to map from the body. Tried body mapping in API-gateway as below,
BODY MAPPING TEMPLATE
{
'Destination': {
ToAddresses:'xxxx#example.com'
},
'Message': {
'Body': {
'Text': {
'Data': 'message body ! hurray !!!',
'Charset': 'UTF-8'
}
},
'Subject': {
'Data': 'subject data',
'Charset': 'UTF-8'
}
},
'Source': 'yyy#example.com'
}
RESPONSE FROM SES
`{
"Output": {
"__type": "com.amazon.coral.service#SerializationException",
"Message": null
},
"Version": "1.0"
}`
Questions
Is it possible to achieve this without using lambda?
Am I using the body mapping correctly?
Could anyone please throw light on how to achieve this? Any help highly appreciated.
I got stuck on the same problem and finally made it. I'll share what I learned.
According to this document, request body should be in form of x-www-form-urlencoded, such as:
Action=SendRawEmail
&Destinations.member.1=allan%40example.com
&RawMessage.Data=RnJvbTp1c2VyQGV4YW1wbGUuY29tDQpTdWJqZWN0OiBUZXN0DQoNCk1lc3 ...
Also you have to set Content-Type:application/x-www-form-urlencoded header. If not, it causes exception. With this header, you can't send request with query parameter(since it includes ?) , you also have to set "Action Type" to "Use path override" and "/" in Integration Request section of API gateway console.
I also confirmed that I could send email with attachments using SendRawEmail action.
So my answer to original questions:
Is it possible to achieve this without using lambda? => Yes!
Am I using the body mapping correctly? => No, you have to use x-www-form-urelencoded request body.
Hope this will help.
In an AWS Lambda code, how can I get the HTTP method (e.g. GET, POST...) of an HTTP request coming from the AWS Gateway API?
I understand from the documentation that context.httpMethod is the solution for that.
However, I cannot manage to make it work.
For instance, when I try to add the following 3 lines:
if (context.httpMethod) {
console.log('HTTP method:', context.httpMethod)
}
into the AWS sample code of the "microservice-http-endpoint" blueprint as follows:
exports.handler = function(event, context) {
if (context.httpMethod) {
console.log('HTTP method:', context.httpMethod)
}
console.log('Received event:', JSON.stringify(event, null, 2));
// For clarity, I have removed the remaining part of the sample
// provided by AWS, which works well, for instance when triggered
// with Postman through the API Gateway as an intermediary.
};
I never have anything in the log because httpMethod is always empty.
The context.httpMethod approach works only in templates. So, if you want to have access to the HTTP method in your Lambda function, you need to find the method in the API Gateway (e.g. GET), go to the Integration Request section, click on Mapping Templates, and add a new mapping template for application/json. Then, select the application/json and select Mapping Template and in the edit box enter something like:
{
"http_method": "$context.httpMethod"
}
Then, when your Lambda function is called, you should see a new attribute in the event passed in called http_method which contains the HTTP method used to invoke the function.
API Gateway now has a built-in mapping template that passes along stuff like http method, route, and a lot more. I can't embed because I don't have enough points, but you get the idea.
Here is a screenshot of how you add it in the API Gateway console:
To get there navigate to AWS Console > API Gateway > (select a resource, IE - GET /home) > Integration Request > Mapping Templates > Then click on application/json and select Method Request Passthrough from dropdown shown in the screenshot above
I had this problem when I created a template microservice-http-endpoint-python project from functions.
Since it creates an HTTP API Gateway, and only REST APIs have Mapping template I was not able to put this work. Only changing the code of Lambda.
Basically, the code does the same, but I am not using the event['httpMethod']
Please check this:
import boto3
import json
print('Loading function')
dynamo = boto3.client('dynamodb')
def respond(err, res=None):
return {
'statusCode': '400' if err else '200',
'body': err.message if err else json.dumps(res),
'headers': {
'Content-Type': 'application/json',
},
}
def lambda_handler(event, context):
'''Demonstrates a simple HTTP endpoint using API Gateway. You have full
access to the request and response payload, including headers and
status code.
To scan a DynamoDB table, make a GET request with the TableName as a
query string parameter. To put, update, or delete an item, make a POST,
PUT, or DELETE request respectively, passing in the payload to the
DynamoDB API as a JSON body.
'''
print("Received event: " + json.dumps(event, indent=2))
operations = {
'DELETE': lambda dynamo, x: dynamo.delete_item(**x),
'GET': lambda dynamo, x: dynamo.scan(**x),
'POST': lambda dynamo, x: dynamo.put_item(**x),
'PUT': lambda dynamo, x: dynamo.update_item(**x),
}
operation = event['requestContext']['http']['method']
if operation in operations:
payload = event['queryStringParameters'] if operation == 'GET' else json.loads(event['body'])
return respond(None, operations[operation](dynamo, payload))
else:
return respond(ValueError('Unsupported method "{}"'.format(operation)))
I changed the code from:
operation = event['httpMethod']
to
operation = event['requestContext']['http']['method']
How do I get this solution?
I simply returned the entire event, checked the JSON and put it to work with the correct format.
If event appears an empty object, make sure you enabled proxy integration for the method. Proxy integration for an HTTP method adds request information into event.
See Use Lambda Proxy integration on API Gateway page.
If you are using API gateway, http method will be automatically passed to the event parameter when the lambda is triggered.
export const handler: Handler<APIGatewayProxyEvent> = async (
event: APIGatewayEvent,
context: Context
): Promise<APIGatewayProxyResult> => {
const httpMethod = event.httpMethod;
...
}