I've written a small lambda function and connected it to the API Gateway. I added an error regex to match .*Error.* and return status 400 in the API Gateway. The problem I face is that the regex seems to match only if the lambda failed, as this thread suggests.
My lambda function:
import logging
import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
if int(event['event_id']) == 1:
return {
'statusCode': 200,
"status": "success"
}
elif int(event['event_id']) == 2:
return {
'statusCode': 400,
"status": "Error"
}
else:
raise Exception("Error")
It looks like case 1 works well, it returns status 200 by default. With event_id=3 it returns status 400 from the API Gateway (with the stack trace in the data, which I would like to avoid), but with event_id=2 it returns status 200 with the Error string in the data.
How can I mark the lambda as failed without throwing an exception?
A failed Lambda means there was some code section in Lambda Function that was failed to be executed. In the current code only the third path has such code section (artificially generated Exception).
It is not possible to execute all your code sections in Lambda and still fail.
If you want to handle API Gateway return status code via program, you'll have to use Lambda Integration. With Lambda integration you may return a response like:
{
"statusCode": "400",
body: json.dumps({ "error": "you messed up!" }),
headers: {
"Content-Type": "application/json",
}
}
and you'll get 400 in API response.
Related
I am currently trying to read my dynamodb table "saved_measurements" with the partition key "Name". I'm doing this through API Gateway Lambda proxy integration, and have tested my
event['pathParameters']['name'] to be working just fine so that shouldn't be the issue.
However, when I query my dynamodb table with my 'Name', the error appears.
{"message": "Internal server error"}
I have referenced many resources for querying dynamodb but nothing seems to work. I have also tried printing a example string within the response body like "yes" and it works with no issues. Only when I attempt to send my data in my response body do I meet that issue again.
What can I try to resolve this?
import json
import boto3
from boto3.dynamodb.conditions import Key, Attr
def lambda_handler(event, context):
client = boto3.resource('dynamodb')
table = client.Table('saved_measurements')
data = table.query(KeyConditionExpression=Key('Name').eq(event['pathParameters']['name']))
stuff = data.Items
response = {
"statusCode": 200,
"headers": {
"Content-Type": 'application/json',
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
"body": json.dumps(stuff),
"isBase64Encoded": False
}
return response
You code needs some optimising, however i'm not certain it will resolve the issue. Let me share how I would write it:
import json
import boto3
from boto3.dynamodb.conditions import Key, Attr
client = boto3.resource('dynamodb')
table = client.Table('saved_measurements')
def lambda_handler(event, context):
status_code=0
stuff = ""
try:
data = table.query(KeyConditionExpression=Key('Name').eq(event['pathParameters']['name']))
stuff = json.dumps(data['Items'])
status_code = 200
except Exception as e:
print(e)
stuff = e
status_code = 400
response = {
"statusCode": status_code,
"headers": {
"Content-Type": 'application/json',
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
"body": stuff,
"isBase64Encoded": False
}
return response
Some points on the code:
Use try/except block anytime you are making API requests to ensure you are not returning exceptions to your downstream tasks unexpectedly
I tend not to use dot notation in Python, hence I changed date.Items to data['Items']. It tends to work more often than dot notation.
Create you clients outside of your request handler in Lambda, this allows Lambda to reuse clients across multiple invocations which will improve your latency.
Don't return 200 for every request regardless on how it resulted, return the correct status code so your downstream processes don't get confused.
Give those changes a go, if you don't succeed, please share your APIGW config.
I use raise Exception (" message ") and it returns this on browser
{"errorMessage": "{"httpStatus": 200, "message": "Exception: {\"httpStatus\": 200, \"message\": \"Exception: [InternalServerError] Project not found\"}"}", "errorType": "Exception", "stackTrace": [the stack trace]}
The stack trace causes security issue
If you are using API gateway in front of lambda. You can set reponse mapping template like below, this will override the response error message and response code as well.
#if($inputRoot.toString().contains('InternalServerError'))
{
"message": "Internal Server Error"
}
#set($context.responseOverride.status = 500)
Alternately you can also catch all the exceptions in the lambda and return whatever you like. However this will not override the status code and you would still get 200 even in case of error.
def handler(event, context):
try:
dosomething(event)
except Exception:
retunrn { "message": "Internal Server error" }
def dosomething(event):
.... You business logic.
From what I understand, when using Lambda proxy integration one must return statusCode and body in order to return any meaningful error information to the HTTP client.
My python code is below:
import json
import traceback
def lambda_handler(event, context):
try:
result = business_logic()
return {
'statusCode': 200,
'body': json.dumps(result)
}
except Exception as e:
error = {
"type": e.__class__.__name__,
"message": str(e),
"traceback": traceback.format_exc()
}
return {
'statusCode': 502,
'body': json.dumps(error)
}
My problem is, I'd like to make use of Lambda and Cloudwatch's handy monitoring interface (e.g. being able to trigger off lambda errors) but since lambda in this code is handling all errors and returning a value, I think lambda will have 0% error rate.
Is there an alternative approach which still uses proxy integration and passes error info?
I think if you want to check your metrics, it's better to check API Gateway's cloudwatch than Lambda's.
Unless you really really want Lambda's cloudwatch to monitor errorrs, you might have through non-proxy integration
It's a bit messier, but since the lambda is responsible for the error handling in the code(response codes/etc.) it might have to also be responsible for creating the metrics than.
You can simply generate the metric via the SDK, for example:
def handler(event: Dict[str, Any], context: LambdaContext) -> Dict:
try:
raise Exception( "")
except Exception:
client = boto3.client('cloudwatch')
response = client.put_metric_data(Namespace=‘my_namespace’, MetricData=[
{
'MetricName': f”my_metric",
'Value': 1,
'Timestamp': time.time()
}
])
return ResponseUtil.build_exception_response(logger)
I am writing an AWS Lambda Python 3.6 function to use as a Lambda proxy on my API in API Gateway. When writing the Lambda, I am calling a helper function, where if there is an error, raises an exception. API Gateway doesn't like this, as it expects "body," "statusCode," and "headers" in the response from the Lambda, and when an exception is raised in Python, those keys are not provided.
I am wondering if it's possible to raise my custom exception with Lambda proxy in mind, so that I can break out of whatever callee I am in and return from the program fluidly, without having to check for errors from the callee in the caller. Basically, I want to raise an exception, provide my status code, headers, and body, and completely return from the Lambda function with API Gateway recognizing the error.
If you're using Lambda Proxy integration, you are responsible for returning a proper response whether its a success or an exception.
You can do so by catching the exception.
def handler(event, context):
try:
return {
'statusCode': 200,
'body': json.dumps({
'hello': 'world'
})
}
except BadRequestError:
return {
'statusCode': 400,
'body': json.dumps({
'error': 'Bad Request Error'
})
}
except:
return {
'statusCode': 500,
'body': json.dumps({
'error': 'Internal Server Error'
})
}
in node.js you can use:
callback(null, RESPONSE_NO_SUCCESS);
where RESPONSE_NO_SUCCESS is like this:
import json
return {
statusCode: 200,
body: json.dumps({YOUR_ERROR_HERE})
};
This should work as you want you just need to look up how the callback works in python
I'm currently writing a REST API for an app I'm working on. The app is written in python using flask. I have the following:
try:
_profile = profile(
name=request.json['name'],
password=profile.get_salted_password('blablabla'),
email=request.json['email'],
created_by=1,
last_updated_by=1
)
except AssertionError:
abort(400)
session = DatabaseEngine.getSession()
session.add(_profile)
try:
session.commit()
except IntegrityError:
abort(400)
The error handler looks like this:
#app.errorhandler(400)
def not_found(error):
return make_response(standard_response(None, 400, 'Bad request'), 400)
I'm using the error 400 to denote both a problem with a sqlalchemy model validator and a unique constraint when writing to the database and in both cases the following error is sent to the client:
{
"data": null,
"error": {
"msg": "Bad request",
"no": 400
},
"success": false
}
Is there a way to still use abort(400) but also set the error somehow so that the error handler can take care of adding additional information for the error object in the result?
I would like it to be more in line with:
{
"data": null,
"error": {
"msg": "(IntegrityError) duplicate key value violates unique constraint profile_email_key",
"no": 400
},
"success": false
}
you can directly put a custom response in abort() function:
abort(make_response("Integrity Error", 400))
Alternatively, you can put it in the error handler function
#app.errorhandler(400)
def not_found(error):
resp = make_response("Integrity Error", 400)
return resp
errorhandler can take an exception type as well:
#app.errorhandler(AssertionError)
def handle_sqlalchemy_assertion_error(err):
return make_response(standard_response(None, 400, err.message), 400)
i know am late to the game, but for anyone who wants another solution, mine is based on the answer by #codegeek.
i was able to accomplish something similar with the following in my ServerResponse.py module:
def duplicate(message=""):
response = make_response()
response.status_code = 409
response.headers = {
"X-Status-Reason" : message or "Duplicate entry"
}
abort(response)
then i can call
ServerResponse.duplicate('Duplicate submission. An article with a similar title already exists.')
this makes it easy in my AngularJS app to check for a response status and display the X-Status-Reason default or customized message