AWS Lambda: key error when sending a POST message - amazon-web-services

I have a very simple problem:
my lamda function works fine as long as i do not write something like "a = event["key"], but a = "test":
This is from Cloudwatch:
#message
[ERROR] KeyError: 'key1' Traceback (most recent call last):
#message
[ERROR] KeyError: 'key1' Traceback (most recent call last): File "/var/task/lambda_function.py", line 5, in lambda_handler a = event["key1]
This is what i have sent with postman (i even tried curl) in the body as raw data:
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
My lamda function looks like this:
import json
def lambda_handler(event, context):
# TODO implement
a = event["key1"]
return {
'statusCode': 200,
'body': json.dumps(a)
}

REST Api LAMBDA will pass the request as is where as LAMBDA_PROXY will append additonal metadata on query parms, api keys, etc. so, the input request body is passed as json string as attribute body. json.loads(event['body']) will give us the actual request body.
More details on changing integration type is here
Below code can extract key1 from input json object for Lambda_Proxy.
import json
def lambda_handler(event, context):
print(event)
a = json.loads(event['body'])['key1']
return {
'statusCode': 200,
'body': json.dumps(a)
}

Fastest way for me was to use a HTTP API and use form-data with key1=test. Then i printed event["body"] and found out that my body was base64 encoded. I used the following code to make that visible:
import json
import base64
def lambda_handler(event, context):
# TODO implement
a = event["body"]
print(a)
message_bytes = base64.b64decode(a)
message = message_bytes.decode('ascii')
return {
'statusCode': 200,
'body': json.dumps(message)
}
The output was:
"----------------------------852732202793625384403314\r\nContent-Disposition: form-data; name=\"key1\"\r\n\r\ntest\r\n----------------------------852732202793625384403314--\r\n"

Related

Django REST API: How to respond to POST request?

I want send POST request with axios(VueJS) and when Django server got a POST request then I want to get back that request message.
I tried make functions when got an POST request in Django server and then return JsonResponse({"response": "got post request"), safe=False)
JS function
sendMessage() {
axios({
method: "POST",
url: url,
data: this.message
})
.then(response => {
this.receive = response;
})
.catch(response => {
alert('Failed to POST.' + response);
})
}
}
views.py
from chat.serializer import chatSerializer
from chat.models import *
from rest_framework.routers import DefaultRouter
from rest_framework import viewsets
from django.http import JsonResponse
from django.views.generic import View
# Create your views here.
class get_post(View):
def post(self, request):
if request.method == 'POST':
JsonResponse({"response": 'got post request'}, safe=False)
but error says like that in django
Internal Server Error: /api/chat/
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/django/core/handlers/exception.py", line
47, in inner
response = get_response(request)
File "/usr/lib/python3/dist-packages/django/core/handlers/base.py", line 188,
in _get_response
self.check_response(response, callback)
File "/usr/lib/python3/dist-packages/django/core/handlers/base.py", line 309,
in check_response
raise ValueError(
ValueError: The view chat.views.views.get_post didn't return an HttpResponse object. It returned None instead.
[26/Oct/2022 17:06:51] "POST /api/chat/ HTTP/1.1" 500 60946
I think POST request is working properly, but Django code is something wrong.
Therefore, my question is..
How to fix and solve this error?
When I call axios in JS, inside '.then' we got a response so which data is come to this variable? Should I return this data like Response() or JsonResponse() method?
just add a return in the view ... like this:
class get_post(View):
def post(self, request):
if request.method == 'POST':
return JsonResponse({"response": 'got post request'}, safe=False)
because the error complains that the post function must return anything.
for the second question ya you need to return JsonResponse because you are dealing with APIs.

python3(lambda function) failing to send response back to cloud formation template

I have created a cloud formation template for cross s3-bucket object replication using lambda which is written in python, Below is the lambda code.
import json
import logging
import signal
import boto3
from urllib.request import *
s3 = boto3.resource('s3')
s3client = boto3.client('s3')
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
def lambda_handler(event, context):
sourcebucketname = event['ResourceProperties']['SourceBucketName']
destinationbucketname = event['ResourceProperties']['DestinationBucketName']
accountid = boto3.client('sts').get_caller_identity()['Account']
try:
LOGGER.info('REQUEST RECEIVED:\n %s', event)
LOGGER.info('REQUEST RECEIVED:\n %s', context)
if event['RequestType'] == 'Create':
LOGGER.info('CREATE!')
response = s3client.list_objects(Bucket=sourcebucketname)
print(response)
for record in response['Contents']:
key = record['Key']
dest_key = key
copy_source = {'Bucket': sourcebucketname, 'Key': key}
destbucket = s3.Bucket(destinationbucketname)
response = destbucket.copy(copy_source, dest_key, ExtraArgs={'ACL':'bucket-owner-full-control'})
print(response)
print('{} transferred to destination bucket'.format(key))
send_response(event, context, "SUCCESS",
{"Message": "Resource creation successful!"})
elif event['RequestType'] == 'Update':
LOGGER.info('UPDATE!')
send_response(event, context, "SUCCESS",
{"Message": "Resource update successful!"})
elif event['RequestType'] == 'Delete':
LOGGER.info('DELETE!')
send_response(event, context, "SUCCESS",
{"Message": "Resource deletion successful!"})
else:
LOGGER.info('FAILED!')
send_response(event, context, "FAILED",
{"Message": "Unexpected event received from CloudFormation"})
except: #pylint: disable=W0702
LOGGER.info('FAILED!')
send_response(event, context, "FAILED", {
"Message": "Exception during processing"})
def send_response(event, context, response_status, response_data):
'''Send a resource manipulation status response to CloudFormation'''
response_body = json.dumps({
"Status": response_status,
"Reason": "See the details in CloudWatch Log Stream: " + context.log_stream_name,
"PhysicalResourceId": context.log_stream_name,
"StackId": event['StackId'],
"RequestId": event['RequestId'],
"LogicalResourceId": event['LogicalResourceId'],
"Data": response_data
})
response_bdy=response_body.encode('utf-8')
LOGGER.info('ResponseURL: %s', event['ResponseURL'])
LOGGER.info('ResponseBody: %s', response_body)
opener = build_opener(HTTPHandler)
request = Request(event['ResponseURL'], data=response_bdy)
request.add_header('Content-Type', '')
request.add_header('Content-Length', len(response_body))
request.get_method = lambda: 'PUT'
response = opener.open(request)
LOGGER.info("Status code: %s", response.getcode())
LOGGER.info("Status message: %s", response.msg)
the s3 objects are getting copied to the destination bucket successfully, but the lambda function is failing to send event responses back to cloud formation. below is the error Im getting.
[ERROR] TypeError: POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str.
Traceback (most recent call last):
File "/var/task/index.py", line 47, in lambda_handler
send_response(event, context, "FAILED", {
File "/var/task/index.py", line 69, in send_response
response = opener.open(request)
File "/var/lang/lib/python3.9/urllib/request.py", line 514, in open
req = meth(req)
File "/var/lang/lib/python3.9/urllib/request.py", line 1277, in do_request_
raise TypeError(msg)
[ERROR] TypeError: POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str. Traceback (most recent call last): File "/var/task/index.py", line 47, in lambda_handler send_response(event, context, "FAILED", { File "/var/task/index.py", line 69, in send_response response = opener.open(request) File "/var/lang/lib/python3.9/urllib/request.py", line 514, in open req = meth(req) File "/var/lang/lib/python3.9/urllib/request.py", line 1277, in do_request_ raise TypeError(msg)
send_response function getting failed with the above error, please help where it's going wrong.
The error message is telling you what's wrong.
[ERROR] TypeError: POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str.
In your code, response_body is a str. You can convert it to bytes by doing response_body.encode('utf-8').

DynamoDB using the Serverless Python Template gives KeyError for body

The code for the lambda function is the following:
import json
import logging
import os
import time
import uuid
import boto3
dynamodb = boto3.resource('dynamodb')
def create(event, context):
data = json.loads(event['body'])
if 'text' not in data:
logging.error("Validation Failed")
raise Exception("Couldn't create the todo item.")
timestamp = str(time.time())
table = dynamodb.Table(os.environ['DYNAMODB_TABLE'])
item = {
'id': str(uuid.uuid1()),
'name': data['text'],
'description': data['text'],
'price': data['text'],
'createdAt': timestamp,
'updatedAt': timestamp,
}
# write the todo to the database
table.put_item(Item=item)
# create a response
response = {
"statusCode": 200,
"body": json.dumps(item)
}
return response
The test using AWS' Lambda's testing feature is:
{
"name": "Masks",
"description": "A box of 50 disposable masks",
"price": "$10"
}
The log output is:
START RequestId: 5cf1c00a-dba5-4ef6-b5e7-b692d8235ffe Version: $LATEST
[ERROR] KeyError: 'body'
Traceback (most recent call last):
  File "/var/task/todos/create.py", line 12, in create
    data = json.loads(event['body'])END RequestId: 5cf1c00a-dba5-4ef6-b5e7-b692d8235ffe
Why is "body" giving me a key error? How do I fix this? The template is directly from www.severless.com, and based off of online tutorials, people have used the exact same code, albie with different values, successfully?
I've tried changing variable names and value to no avail.
sls deploy
Does successfully create the table, but I am unable to input any data into it.
Edit 1: For those of you unfamiliar with AWS' Lambda Test feature, using Postman to input the same data is leading either to a 502 Gateway Error.
Assuming that this is the correct event object:
{
"name": "Masks",
"description": "A box of 50 disposable masks",
"price": "$10"
}
your code which matches this event should be:
import json
import logging
import os
import time
import uuid
import boto3
dynamodb = boto3.resource('dynamodb')
def create(event, context):
timestamp = str(time.time())
table = dynamodb.Table(os.environ['DYNAMODB_TABLE'])
item = {
'id': str(uuid.uuid1()),
'name': event['name'],
'description': event['description'],
'price': event['price'],
'createdAt': timestamp,
'updatedAt': timestamp,
}
# write the todo to the database
table.put_item(Item=item)
# create a response
response = {
"statusCode": 200,
"body": json.dumps(item)
}
return response

Requesting help triggering a lambda function from DynamoDB

I was following the guide posted here on youtube https://www.youtube.com/watch?v=jgiZ9QUYqyM and is defiantly what I want. I posted the code that I had for mine and the image of what everything looks like in my AWS.
I have a dynamodb table and linked it to my s3 bucket with a trigger. That trigger is giving me some error message which is posted above. "Decimal('1') is not JSON serializable". Though I was testing it with the helloworld.
This is the code :
import boto3
import json
import os
s3 = boto3.client('s3')
ddb = boto3.resource('dynamodb')
table = ddb.Table('test_table')
def lambda_handler(event, context):
response = table.scan()
body = json.dumps(response['Items'])
response = s3.put_object(Bucket='s3-testing',
Key = 's3-testing.json' ,
Body=body,
ContentType='application/json')
Can someone point me in the right direction? These are the snippets I got
https://i.stack.imgur.com/I0jAn.png
https://i.stack.imgur.com/2hMc9.png
This is the execution log:
Response:
{
"stackTrace": [
[
"/var/task/lambda_function.py",
20,
"lambda_handler",
"body = json.dumps(response['Items'])"
],
[
"/usr/lib64/python2.7/json/__init__.py",
244,
"dumps",
"return _default_encoder.encode(obj)"
],
[
"/usr/lib64/python2.7/json/encoder.py",
207,
"encode",
"chunks = self.iterencode(o, _one_shot=True)"
],
[
"/usr/lib64/python2.7/json/encoder.py",
270,
"iterencode",
"return _iterencode(o, 0)"
],
[
"/usr/lib64/python2.7/json/encoder.py",
184,
"default",
"raise TypeError(repr(o) + \" is not JSON serializable\")"
]
],
"errorType": "TypeError",
"errorMessage": "Decimal('1') is not JSON serializable"
}
Function log:
START RequestId: 31719509-94c7-11e8-a0d4-a9b76b7b212c Version: $LATEST
Decimal('1') is not JSON serializable: TypeError
Traceback (most recent call last):
File "/var/task/lambda_function.py", line 20, in lambda_handler
body = json.dumps(response['Items'])
File "/usr/lib64/python2.7/json/__init__.py", line 244, in dumps
return _default_encoder.encode(obj)
File "/usr/lib64/python2.7/json/encoder.py", line 207, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib64/python2.7/json/encoder.py", line 270, in iterencode
return _iterencode(o, 0)
File "/usr/lib64/python2.7/json/encoder.py", line 184, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: Decimal('1') is not JSON serializable
Decimal object is not json serializable. Considering casting the Decimal into a float using a helper function. (json.dumps() takes a default function)
import boto3
import json
import os
from decimal import Decimal
s3 = boto3.client('s3')
ddb = boto3.resource('dynamodb')
table = ddb.Table('test_table')
def lambda_handler(event, context):
response = table.scan()
body = json.dumps(response['Items'], default=handle_decimal_type)
response = s3.put_object(Bucket='s3-testing',
Key = 's3-testing.json' ,
Body=body,
ContentType='application/json')
def handle_decimal_type(obj):
if isinstance(obj, Decimal):
if float(obj).is_integer():
return int(obj)
else:
return float(obj)
raise TypeError
The problem is that the Dynamo Python library is converting numeric values to Decimal objects, but those aren't JSON serializable by default, so json.dumps blows up. You will need to provide json.dumps with a converter for Decimal objects.
See Python JSON serialize a Decimal object

Post query to API in Django REST

I have code in Angular 2:
sendPost(){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
let requestOptions = new RequestOptions({headers: headers});
requestOptions.headers = headers;
let data = {
"name": "XX",
"email": "xxx#op.com",
"phone_number": "+99999995555",
"address": "YYY",
"code": "80-885",
"city": "YHYY",
"voivodeship": "ZZZZ",
"description": "VVVVV"
};
this.http.post(`http://127.0.0.1:8000/companies/create`, data, requestOptions).subscribe(res => {
console.log(res.json());
}, (err) => {
console.log(err);
});
}
Error API:
<WSGIRequest: OPTIONS '/companies/create'>
Internal Server Error: /companies/create
Traceback (most recent call last):
File "C:XX\CRM\env\lib\site-packages\django\core\handlers\exception.py", line 41, in inner
response = get_response(request)
File "C:XX\CRM\env\lib\site-packages\django\core\handlers\base.py", line 198, in _get_response
"returned None instead." % (callback.__module__, view_name)
ValueError: The view my_crm.views.companies_create didn't return an HttpResponse object. It returned None instead.
[25/Jul/2017 21:32:35] "OPTIONS /companies/create HTTP/1.1" 500 59515
The API shows that there is an error somewhere in the function but when I use POSTMAN this is identical JSON goes through with no problem. Where can be the error?
I think the API is well handled when testing using POSTMAN.
EDIT:
API function:
#csrf_exempt
def companies_create(request):
"""
Create a new company.
"""
if request.method == 'POST':
data = JSONParser().parse(request)
serializer = CompanySerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
The solution is to replace:
headers.append('Content-Type', 'application/json');
to:
headers.append('Content-Type', 'application/x-www-form-urlencoded');
I'm not able to explain this because I'm sending a JSON file. Maybe someone will explain what this is about?