LoginSignup
7
6

More than 3 years have passed since last update.

API GatewayでLambdaのエラーハンドリング

Last updated at Posted at 2020-07-20

題材とするアーキテクチャ

image.png

API Gatewayの返り値を整形する。

エラー時に400/500台のステータスコードを付してDescriptionをつけて返したい。

1 Exceptionクラスを継承して拡張した例外クラスを作る。
2 エラー時にraiseする。

Lambda(1)
import json
import boto3
import logging
import traceback

logger = logging.getLogger()
logger.setLevel(logging.INFO)

dynamodb = boto3.resource('dynamodb')

# 例外クラスを拡張
class ExtendException(Exception):
    def __init__(self, statusCode, description):
        self.statusCode = statusCode
        self.description = description

    def __str__(self):
        obj = {
            "statusCode": self.statusCode,
            "description": self.description
        }
        return json.dumps(obj)

def lambda_handler(event, context):

    # validation check
    if not "key" in event or not isinstance(event["key"], int):
        raise ExtendException(400, "Bad Request")

    # 何らかの処理

    return 0

テスト

response
{
  "errorMessage": "{\"statusCode\": 400, \"description\": \"Bad Request\"}",
  "errorType": "ExtendException",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 29, in lambda_handler\n    raise ExtendException(400, \"Bad Request\")\n"
  ]
}

3 API Gatewayのレスポンスをマッピング

image.png
image.png

Lambdaエラーの正規表現では.*statusCode: 400,.*を指定。
Lambdaから返されるエラーレスポンスのerrorMessageの部分のみ返している。

テスト

response
{
  "statusCode": 400,
  "description": "Bad Request"
}

別Lambdaからコールした時にエラー内容を表示する。

以下のようにしてエラーレスポンスを拾うことができる。

try:
    pass
except urllib.error.HTTPError as err:
    res = err.read().decode("utf-8")
Lambda(2)
import json
import urllib.request, urllib.error
import logging
import traceback

logger = logging.getLogger()
logger.setLevel(logging.INFO)

class ExtendException(Exception):
    def __init__(self, statusCode, description):
        self.statusCode = statusCode
        self.description = description

    def __str__(self):
        obj = {
            "statusCode": self.statusCode,
            "description": self.description
        }
        return json.dumps(obj)

request_url = "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1"
api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

def lambda_handler(event, context):
    try:
        headers = {'x-api-key': api_key, "Content-Type":"application/json"}
        method = "POST"

        # validation errorするようにAPIをコールする。
        request_json = {
            "notExistKey": 1
        }

        req = urllib.request.Request(url=request_url, method=method, headers=headers, data=json.dumps(request_json).encode())

        with urllib.request.urlopen(req) as res:
            body = res.read().decode("utf-8")

    except urllib.error.HTTPError as err:
        traceback.print_exc()
        res = err.read().decode("utf-8")
        res = json.loads(res)
        logger.info("------------------")
        logger.info(res)
        logger.info("------------------")
        raise ExtendException(500, "[ERROR] {} : {}".format(res["statusCode"], res["description"]))

    except urllib.error.URLError as err:
        traceback.print_exc()
        res = err.read().decode("utf-8")
        res = json.loads(res)
        logger.info("------------------")
        logger.info(res)
        logger.info("------------------")
        raise ExtendException(500, "[ERROR] {} : {}".format(res["statusCode"], res["description"]))
    except:
        traceback.print_exc()
        raise ExtendException(500, "Internal Error")

    return json.loads(body)

テスト

response
{
  "errorMessage": "{\"statusCode\": 500, \"description\": \"[ERROR] 400 : Bad Request\"}",
  "errorType": "ExtendException",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 45, in lambda_handler\n    raise ExtendException(500, \"[ERROR] {} : {}\".format(res[\"statusCode\"], res[\"description\"]))\n"
  ]
}

追記 2020/08/06

Lambdaのタイムアウトをハンドリングする。

上記の設定ではLambda(1)でタイムアウトした際にステータスコード200が返ってしまう。

{
  "errorMessage": "2020-08-06T13:36:57.311Z 312a1819-af1a-4fc5-bb9c-f8ed8f573a9b Task timed out after 3.00 seconds"
}

統合レスポンスの正規表現では以下のように指定することでLambdaのタイムアウトをハンドリングできる。

.*"statusCode": 500,.*|.*Task timed out after.*
7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6