web-dev-qa-db-ja.com

API Gateway + Lambda + Python:例外の処理

非プロキシモードでAPI GatewayからPythonベースのAWS Lambdaメソッドを呼び出しています。例外の一部を使用してJSON本体とともに適切なHTTPステータスコードが設定されるように、例外を適切に処理する方法を教えてください。

例として、次のハンドラがあります。

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です。

18

tl; dr

  1. 200以外の応答が必要な場合は、Lambdaハンドラーが例外をスローする必要があります。
  2. ハンドラーメソッドのすべての例外をキャッチします。キャッチした例外メッセージをJSONにフォーマットし、カスタムの例外タイプとしてスローします。
  3. 統合応答を使用して、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ゲートウェイ統合応答

概観

「統合応答」は、Lambdaからの応答をHTTPコードにマップします。また、通過するメッセージ本文を変更できます。

デフォルトでは、「200」統合応答が構成されています。これにより、シリアル化されたJSON例外を含め、Lambdaからのすべての応答がそのままHTTP 200(OK)応答としてクライアントに渡されます。

適切なメッセージの場合、「200」の統合応答を使用して、JSONペイロードを定義済みのモデルの1つにマッピングすることができます。

例外をキャッチする

例外については、適切なHTTPステータスコードを設定し、おそらくスタックトレースを削除して、コードの内部を非表示にする必要があります。

返すHTTPステータスコードごとに、「統合応答」エントリを追加する必要があります。統合応答は、errorMessageと一致する正規表現一致(Java.util.regex.Matcher.matches()ではなく.find()を使用)で構成されていますフィールド。一致したら、ボディマッピングテンプレートを設定して、適切な例外ボディを選択的にフォーマットできます。

正規表現は例外からのerrorMessageフィールドに対してのみ一致するため、異なる統合応答が一致し、それに応じてエラーを設定します。 (_.*_を使用してすべての例外と一致させることはできません。これは例外を除くすべての応答と一致するように見えるためです!)

意味のある例外

メッセージに十分な詳細を含む例外を作成するには、 error-handling-patterns-in-Amazon-api-gateway-and-aws-lambda ブログでハンドラーに例外ハンドラーを作成して、例外メッセージで使用されるJSON文字列への例外の詳細。

私が好むアプローチは、API Gatewayへの応答を処理するハンドラーとして新しいtopメソッドを作成することです。このメソッドは、必要なペイロードを返すか、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. 編集 "メソッド応答"と新しいモデルを_400_にマッピング
  3. 新規追加統合応答
  4. コードを_400_に設定します
  5. 「ClientException」タイプと空白の許容範囲が一致するように正規表現を設定:_.*"type"\s*:\s*"ClientException".*_
  6. _application/json_のボディマッピングテンプレートを追加して、errorMessageのコンテンツをモデルにマッピングします。

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