Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to throw HTTP error code with AWS Lambda using Lambda Proxy?

I created an AWS Lambda function using Python 3.8 with a Lambda Proxy API Gateway trigger: API Gateway

It is indeed possible to return custom HTTP error codes:

def lambda_handler(event, context):
    return {
        'statusCode': 400,
        'body': json.dumps('This is a bad request!')
    }

However, some online examples (e.g. 1, 2) simply raise an Exception to return the error with a custom message. If an Exception is thrown on the handler as below, the server return a 502 as the response is not in the expected format for Proxy Integration:

def lambda_handler(event, context):
    raise Exception('This is an exception!')

I think the examples rely on some Integration Response templates and do not use the Proxy Integration. Is it possible to achieve the same thing with Lambda Proxy? I'd like to avoid to globally catch exceptions in the Lambda handler to build custom error responses.

like image 411
DurandA Avatar asked Feb 14 '20 11:02

DurandA


Video Answer


3 Answers

DurandA - I believe you are absolutely correct: the simplified Lambda Proxy Integration approach relies on you catching your exceptions and returning the standardized format:

def lambda_handler(event, context):
    return {
        'statusCode': 400,
        'body': json.dumps('This is a bad request!')
    }

The simplified Lambda Proxy Integration feature was announced in a September 2016 blog post, but one of the examples you cited was posted earlier, in a June 2016 blog post, back when the more complicated Integration Response method was the only way. Maybe you have stumbled on an out of date example.

You also posted a link to the product documentation for error handling, at the top in the section covering the Lambda proxy integration feature, it says:

With the Lambda proxy integration, Lambda is required to return an output of the following format:

{
  "isBase64Encoded" : "boolean",
  "statusCode": "number",
  "headers": { ... },
  "body": "JSON string"
}

Here is a working example that returns a HTTP 400 with message "This is an exception!" using Lambda Proxy Integration.

import json

def exception_handler(e):
    # exception to status code mapping goes here...
    status_code = 400
    return {
        'statusCode': status_code,
        'body': json.dumps(str(e))
    }

def lambda_handler(event, context):
    try:
        raise Exception('This is an exception!')
        return {
            'statusCode': 200,
            'body': json.dumps('This is a good request!')
        }
    except Exception as e:
        return exception_handler(e)

Output from the above:

$ http https://**********.execute-api.us-east-2.amazonaws.com/test
HTTP/1.1 400 Bad Request
Connection: keep-alive
Content-Length: 23
Content-Type: application/json
Date: Sun, 23 Feb 2020 05:06:59 GMT
X-Amzn-Trace-Id: Root=1-********-************************;Sampled=0
x-amz-apigw-id: ****************
x-amzn-RequestId: ********-****-****-****-************

"This is an exception!"

I understand your frustration that you do not want to build a custom exception handler. Fortunately, you only have to build a single handler wrapping your lambda_handler function. Wishing you all the best!

like image 50
Chuck T. Avatar answered Oct 23 '22 01:10

Chuck T.


As far as I know, using the language built in Exceptions won't work because the API Gateway expects a precise response format, which is a JSON. What you can do is still raise the exception so it can be logged in CloudWatch, but then wrap it inside a JSON object so you can also return it to the client if is needed. I really think this should be possible out of the box, since with ECS services you can do this (I use some services in .NET Core and when an Exception occurs I get the appropriate status code as a response, without doing extra work)

like image 26
Jimi Avatar answered Oct 23 '22 03:10

Jimi


For simple cases, the generic approach of Chuck T. is probably OK. But if you have complex workflow with several coordinated Lambda functions and AWS state machine, you can use more flexible strategy described here.

  1. In your functions, create many simple custom exception classes like this: class SecretAccessFailedException(Exception): pass.
  2. Raise those custom exceptions as needed.
  3. Catch them in your state machine and process flexibly.
like image 34
Serhii Kushchenko Avatar answered Oct 23 '22 03:10

Serhii Kushchenko