API Gateway + Lambda + Python:处理异常



我在非代理模式下从API网关调用基于python的AWS Lambda方法。我应该如何正确地处理异常,以便使用异常的部分设置适当的HTTP状态码和JSON体。

作为一个例子,我有以下处理程序:
def my_handler(event, context):
    try:
        s3conn.head_object(Bucket='my_bucket', Key='my_filename')
    except botocore.exceptions.ClientError as e:
        if e.response['Error']['Code'] == "404":
            raise ClientException("Key '{}' not found".format(filename))
            # or: return "Key '{}' not found".format(filename) ?
class ClientException(Exception):
    pass

应该抛出异常还是返回字符串?那么我应该如何配置集成响应?显然我有RTFM,但FM是FU

br

  1. 如果你想要一个非200的响应,你的Lambda处理程序必须抛出一个异常。捕获处理程序方法中的所有异常。将捕获的异常消息格式化为JSON,并作为自定义异常类型抛出。
  2. 使用集成响应来正则化在Lambda响应的errorMessage字段中找到的自定义异常。

API网关+ AWS Lambda异常处理

关于Lambda、API Gateway以及它们是如何协同工作的,你需要知道很多事情。

λ异常

当一个异常从你的处理程序/函数/方法抛出时,异常被序列化成JSON消息。在您的示例代码中,对于来自S3的404,您的代码将抛出:

{
  "stackTrace": [
      [
          "/var/task/mycode.py",
          118,
          "my_handler",
          "raise ClientException("Key '{}' not found ".format(filename))"
      ]
  ],
  "errorType": "ClientException",
  "errorMessage": "Key 'my_filename' not found"
}

API网关集成响应

概述

"Integration Responses"将Lambda的响应映射到HTTP代码。它们还允许在传递时更改消息体。

默认情况下,为您配置了一个"200"集成响应,它将所有来自Lambda的响应原样传递回客户端,包括序列化的JSON异常,作为HTTP 200 (OK)响应。

对于好的消息,您可能希望使用"200"集成响应将JSON有效负载映射到您定义的模型之一。

捕获异常

对于异常,您将需要设置适当的HTTP状态码,并可能删除堆栈跟踪以隐藏代码的内部。

对于您希望返回的每个HTTP状态码,您将需要添加一个"集成响应"条目。集成响应配置了一个与errorMessage字段匹配的regex匹配(使用java.util.regex.Matcher.matches()而不是.find())。一旦匹配完成,你就可以配置一个Body Mapping Template,来选择性地格式化一个合适的异常体。

由于regex只匹配异常中的errorMessage字段,因此您需要确保异常包含足够的信息,以允许不同的集成响应匹配并相应地设置错误。(你不能使用.*来匹配所有的异常,因为这似乎匹配所有的响应,包括非异常!)

含义

的异常

要在其消息中创建具有足够详细信息的异常,error-handling-patterns-in-amazon-api-gateway-and-aws-lambda博客建议您在处理程序中创建一个异常处理程序,将异常的详细信息填充到一个JSON字符串中,以便在异常消息中使用。

我更喜欢的方法是创建一个新的top方法作为处理程序,它处理响应API网关。这个方法要么返回所需的有效负载,要么抛出一个异常,其中包含一个编码为JSON字符串的原始异常,作为异常消息。

def my_handler_core(event, context):
    try:
        s3conn.head_object(Bucket='my_bucket', Key='my_filename')
        ...
        return something
    except botocore.exceptions.ClientError as e:
        if e.response['Error']['Code'] == "404":
            raise ClientException("Key '{}' not found".format(filename))
def my_handler(event=None, context=None):
    try:
        token = my_handler_core(event, context)
        response = {
            "response": token
        }
        # This is the happy path
        return response
    except Exception as e:
        exception_type = e.__class__.__name__
        exception_message = str(e)
        api_exception_obj = {
            "isError": True,
            "type": exception_type,
            "message": exception_message
        }
        # Create a JSON string
        api_exception_json = json.dumps(api_exception_obj)
        raise LambdaException(api_exception_json)
# Simple exception wrappers
class ClientException(Exception):
    pass
class LambdaException(Exception):
    pass

在异常情况下,Lambda现在将返回:

{
    "stackTrace": [
        [
            "/var/task/mycode.py",
            42,
            "my_handler",
            "raise LambdaException(api_exception_json)"
        ]
    ],
    "errorType": "LambdaException",
    "errorMessage": "{"message": "Key 'my_filename' not found", "type": "ClientException", "isError": true}"
}

映射异常现在您已经在errorMessage中拥有了所有的详细信息,您可以开始映射状态码并创建格式良好的错误有效负载。API Gateway解析并取消errorMessage字段的转义,因此使用的正则表达式不需要处理转义。 例子

要捕获此ClientException为400错误并将有效负载映射到干净的错误模型,您可以执行以下操作:

  1. 创建新的错误模型:

    {
      "type": "object",
      "title": "MyErrorModel",
      "properties": {
        "isError": {
            "type": "boolean"
        },
        "message": {
          "type": "string"
        },
        "type": {
          "type": "string"
        }
      },
      "required": [
        "token",
        "isError",
        "type"
      ]
    }
    
  2. 编辑"Method Response"并将新模型映射到400
  3. 添加新的集成响应
  4. 设置代码为400
  5. 设置regex匹配"ClientException"类型,并允许空白:.*"type"s*:s*"ClientException".*
  6. application/json添加Body Mapping Template以将errorMessage的内容映射到您的模型:

    #set($inputRoot = $input.path('$'))
    #set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))
    {
        "isError" : true,
        "message" : "$errorMessageObj.message",
        "type": "$errorMessageObj.type"
    }
    

相关内容

  • 没有找到相关文章

最新更新