I have an API that is connected to a lambda function that has queryStringParameters. On the function end I have
VARIABLE = event['queryStringParameters']['variable']
When I deploy my API and try to use it "api_url"?variable=something,
I get {"message": "Internal server error"}.
In Cloudwatch I get:
[ERROR] KeyError: 'queryStringParameters'
Traceback (most recent call last):
File "/var/task/index.py", line 217, in handler
VARIABLE = event['queryStringParameters']['variable']
To help troubleshoot I print the event. In Cloudwatch it does, and it appears as "{}", so pretty much as empty.
When I test the function in the console I use the event:
{ "queryStringParameters": {"variable": "T"}}
and the function works just fine.
I've made APIs that are connected to lambda functions before almost identical to this and have had no problem. I'm stumped. Any advice is appreciated.
Based on this AWS guide, you can access the query string parameters using get methods of the event object.
query_string = event.get('queryStringParameters')
if query_string is not None:
name = query_string.get('variable')
In your lambda's case, its quite strange that no queryStringParameters key could be found on the event object. It would be interesting to see if there's an actual parameter sent to the REST API or if the positioning of parameters in the handler's parameters are correct and that the parameter is provided on all requests.
def event_handler(event, context): # Make sure event goes first
query_string = event.get('queryStringParameters')
if query_string is None:
// Return 400 / 422 here to indicate a bad request
Related
I have 3 webhooks that calls my API Gateway which calls my Lambda Function.
url/webhook/....
I want each webhook to call its own python method
startDelivery --> def start_delivery(event, context):
UpdateStatus--> def update_status(event, context):
EndDelivery--> def end_delivery(event, context):
I understand most likely one method will be executed via "url/webhook" which calls the appropriate python method.
def Process_task to call one of the three
What is the ideal way to set up this structure?
Creating different urls for Webhooks and API Gateway captures it and somehow calls the handler?
url/webhook/start
url/webhook/status
url/webhook/end
Sending a different query string for each webhook? and in the lamba parse the query string and call the right python method?
Keep in mind that a Lambda function has one handler (=> 1 invocation = 1 method called).
You can achieve the 1 route <-> 1 method by doing one of the following:
You have a single Lambda function triggered by your 3 APIGW routes.
You can then add a simple router to your function which parse the event['path'] and call the appropriate method.
def lambda_handler(event, context):
path = event['path']
if path == '/webhook/start':
return start_delivery(event, context)
elif path == '/webhook/status':
return update_status(event, context)
elif path == '/webhook/end':
return end_status(event, context)
else:
return { "statusCode": 404, "body": "NotFound" }
Create 1 Lambda function by route:
webhook/start triggers the StartDelivery Lambda function with start_delivery as handler
webhook/status triggers the UpdateDelivery Lambda function with update_delivery as handler
webhook/end triggers the EndDelivery Lambda function with end_delivery as handler
You can use Infrastructure as Code (Cloudformation) to easily manage these functions (SAM: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started-hello-world.html)
acorbel's answer was helpful for me. The latest version is Format 2.0. Many fields have changed. For example event['path'] doesn't exist with Format 2.0.
Please check the below link for correct key names of event structure.
Working with AWS Lambda proxy integrations for HTTP APIs
Im trying to create a simple API gateway in which, with a POST method to a certain endpoint, a lambda function is executed.
Setting that up was easy enough, but I'm having some trouble with the request/response handling. Im sending the following request to the API Gateway (Im using python 3.7).
payload = {
"data": "something",
"data2": "sometsadas"
}
response = requests.post('https://endpoint.com/test', params = payload)
That endpoint activates a lambda function when accesed. That function just returns the same event it received.
import json
def lambda_handler(event, context):
# TODO implement
return event
How can I make it so the return value of my lambda function is actually the response from the request? (Or at least a way in which the return value can be found somewhere inside the response)
Seems it was a problem with how the information is sent, json format is required. Solved it by doing the following in the code.
payload{'data': 'someData'}
config_response = requests.post(endpointURL, data = json.dumps(config_payload))
I have been trying to find the answer to this but am unable to find it anywhere. On the Cloud Functions section in the Google Cloud Platform console there is a section title 'Testing' but I have no idea what one is supposed to put here to test the function, i.e. syntax.
I have attached an image for clarity:
Any help would be much appreciated.
HTTPS Callable functions must be called using the POST method, the Content-Type must be application/json or application/json; charset=utf-8, and the body must contain a field called data for the data to be passed to the method.
Example body:
{
"data": {
"aString": "some string",
"anInt": 57,
"aFloat": 1.23,
}
}
If you are calling a function by creating your own http request, you may find it more flexible to use a regular HTTPS function instead.
Click Here for more information
Example with the Cloud Function default Hello_World that is inserted automatically whenever you create a new Cloud Function:
def hello_world(request):
"""Responds to any HTTP request.
Args:
request (flask.Request): HTTP request object.
Returns:
The response text or any set of values that can be turned into a
Response object using
`make_response <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>`.
"""
request_json = request.get_json()
if request.args and 'message' in request.args:
return request.args.get('message')
elif request_json and 'message' in request_json:
return request_json['message']
else:
return f'Hello World!'
Must be tested with a json as the input args:
{
"message": "Hello Sun!"
}
Out in the Testing Tab:
Hello Sun!
In the Testing tab editor: since we give the function the args in form of a json as we would elsewise write them like in python3 -m main.py MY_ARG, and since "message" is a key of that json, it is found by the elif and returns the value of the dictionary key as the message, instead of "Hello World". If we run the script without json args, else: is reached in the code, and output is "Hello World!":
This looks to be the same as gcloud functions call, with the JSON required being the same as the --data provided in the CLI.
You can check the docs for examples using the CLI, and the CLI documentation itself for further details.
There are multiple ways you could test you cloud function.
1) Use a google emulator locally if you want to test your code before deployment.
https://cloud.google.com/functions/docs/emulator.
This would give you a similar localhost HTTP endpoint that you can send request to for testing your function.
2) Using GUI on deployed function: The triggering event is the json object that the function expects in the request body. For example:
{
"key": "value"
}
Based on your function code dependency for the request it should trigger the function.
Simple Tests for Cloud Pub/Sub:
{"data":"This is data"}
Base64 'Hello World !' message :
{"data":"SGVsbG8gV29ybGQgIQ=="}
I have a simple lambda function that returns a dict response and another lambda function invokes that function and prints the response.
lambda function A
def handler(event,context):
params = event['list']
return {"params" : params + ["abc"]}
lambda function B invoking A
a=[1,2,3]
x = {"list" : a}
invoke_response = lambda_client.invoke(FunctionName="monitor-workspaces-status",
InvocationType='Event',
Payload=json.dumps(x))
print (invoke_response)
invoke_response
{u'Payload': <botocore.response.StreamingBody object at 0x7f47c58a1e90>, 'ResponseMetadata': {'HTTPStatusCode': 202, 'RequestId': '9a6a6820-0841-11e6-ba22-ad11a929daea'}, u'StatusCode': 202}
Why is the response status 202? Also, how can I get the response data from invoke_response? I could not find a clear documentation of how to do it.
A 202 response means Accepted. It is a successful response but is telling you that the action you have requested has been initiated but has not yet completed. The reason you are getting a 202 is because you invoked the Lambda function asynchronously. Your InvocationType parameter is set to Event. If you want to make a synchronous call, change this to RequestResponse.
Once you do that, you can get the returned data like this:
data = invoke_response['Payload'].read()
try: data = invoke_response['Payload'].read()
read() because it is a StreamingBody object
<botocore.response.StreamingBody object at 0x110b91c50>
It is in the boto3 docs. You can find more details about this here: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html#actions
Background
I have a service A accessible with HTTP requests. And I have other services that want to invoke these APIs.
Problem
When I test service A's APIs with POSTMAN, every request works fine. But when I user python's requests library to make these request, there is one PUT method that just won't work. For some reason, the PUT method being called cannot receive the data (HTTP body) at all, though it can receive headers. On the other side, the POST method called in the same manner receives the data perfectly.
I managed to achieve my goal simply by using httplib library instead, but I am still quite baffled by what exactly happened here.
The Crime Scene
Route 1:
#app.route("/private/serviceA", methods = ['POST'])
#app.route("/private/serviceA/", methods = ['POST'])
def A_create():
# request.data contains correct data that can be read with request.get_json()
Route 2:
#app.route("/private/serviceA/<id>", methods = ['PUT'])
#app.route("/private/serviceA/<id>/", methods = ['PUT'])
def A_update(id):
# request.data is empty, though request.headers contains headers I passed in
# This happens when sending the request with Python requests library, but not when sending with httplib library or with POSTMAN
# Also, data comes in fine when all other routes are commented out
# Unless all other routes are commented out, this happens even when the function body has only one line printing request.data
Route 3:
#app.route("/private/serviceA/schema", methods = ['PUT'])
def schema_update_column():
# This one again works perfectly fine
Using POSTMAN:
Using requests library from another service:
#app.route("/public/serviceA/<id>", methods = ['PUT'])
def A_update(id):
content = request.get_json()
headers = {'content-type': 'application/json'}
response = requests.put('%s:%s' % (router_config.HOST, serviceA_instance_id) + '/private/serviceA/' + str(id), data=json.dumps(content), headers = headers)
return Response(response.content, mimetype='application/json', status=response.status_code)
Using httplib library from another service:
#app.route('/public/serviceA/<id>', methods=['PUT'])
def update_course(id):
content= request.get_json()
headers = {'content-type': 'application/json'}
conn = httplib.HTTPConnection('%s:%s' % (router_config.HOST, serviceA_instance_id))
conn.request("PUT", "/private/serviceA/%s/" % id, json.dumps(content), headers)
return str(conn.getresponse().read())
Questions
1. What am I doing wrong for the route 2?
2. For route 2, the handler doesn't seem to be executed when either handler is commented out, which also confuses me. Is there something important about Flask that I'm not aware of?
Code Repo
Just in case some nice ppl are interested enough to look at the messy undocumented code...
https://github.com/fantastic4ever/project1
The serviceA corresponds to course service (course_flask.py), and the service calling it corresponds to router service (router.py).
The version that was still using requests library is 747e69a11ed746c9e8400a8c1e86048322f4ec39.
In your use of the requests library, you are using requests.post, which is sending a POST request. If you use requests.put then you would send a PUT request. That could be the issue.
Request documentation