こんにちは。DSOC R&Dグループの高橋寛治です。
WebAPIの勉強をしなければ! と感じる機会があり、思い立ったが吉日と、早速書籍『Web API: The Good Parts』を購入して学んでいるところです。
シリーズ3回目となる今回は、AWS Lambdaを利用したAPI開発の際のエラー処理について紹介します。
APIに求められるエラー処理
WebAPIを簡単に説明すると、WebAPIとはHTTPプロトコルにより提供されるAPIです。一般的にJSONが、クライアントとサーバー間で受け渡しされます。
どのようなエラー処理が必要になるかを考えてみます。
例えば、無効なJSONがリクエストされた際に、どのような情報があるといいでしょうか。
GithubのAPIの例ですと、HTTPステータスコードは400 Bad Request
を返し、JSONのmessage
にエラーの詳細を記述しています(以下のコードは、Githubのマニュアルから引用しています)。
HTTP/1.1 400 Bad Request
Content-Length: 35
{"message": "Problems parsing JSON"}
HTTPステータスコードで、リクエスト内容にエラーの要因があることが分かり、message
を読むことで無効なJSONを投げたことが原因であると分かります。
HTTPステータスコードとmessage
の役割を掘り下げると、HTTPステータスコードはクライアントのプログラムが受け取り、次の処理を定めるものと考えられます。それに対して、message
は、開発者が読み解くことのできるものとなります。
さまざまなWebAPIのエラー表現をまとめた素晴らしい記事がQiitaにありますので、先人の力を借りて、良いAPIを設計しましょう。
今回、作成するAPIは以下の仕様としました。
- HTTPステータスコード
- 200:成功
- 404:無効なリクエスト
- 500:内部エラー発生
- JSONの形式
{
"message": "メッセージ",
"hogehogeResult": [何らかの結果]
}
AWS LambdaとAPI Gatewayでどのようにエラー処理を行うか
API Gatewayでは、Lambda関数が送出する例外に応じて、HTTPステータスコードおよびメッセージを設定できます。
以下のスクリーンショットは、API GatewayのPOSTエンドポイントの管理画面です。
対応付けたいHTTPステータスコードの定義は、メソッドレスポンスで行います。
以下の図は、メソッドレスポンスに400, 404, 500を追加したものです(200はデフォルトです)。
次に、設定したHTTPステータスコードをLambdaの例外にひも付けます。ひも付けは、Lambdaエラーに対して正規表現マッチングを行います。
具体的には、PythonのExceptionクラスのstr
に設定された文字列表現が、正規表現の対象となります。
以下の図は、HTTPステータスコード404に対して、^[404:NotFound]$
をひも付けたものです。正規表現のエスケープに注意してください。
Pythonのコードでは、Exceptionクラスを継承し、str
に対して、^[404:NotFound]$
を記述します。
class NotFoundError(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return "[404:NotFound]"
ベストプラクティスかどうかは分かりませんが、404や500といった接頭辞をstr
に記述することで、正規表現として取り扱いやすく設定しました。
Lambda関数は実行時間の制限もあるため、制限時間を超える想定外の処理はエラーとしてハンドリングさせてみます。
正規表現は、(^[500:InternalServerError].|.Task timed out.*)
となり、[500:InternalServerError]
から始まるエラー、もしくはTask timed out.
が含まれるエラーの場合に500エラーとなります。
戻り値のJSONは、本文マッピングテンプレートにより設定可能です。本文マッピングテンプレートのリファレンスを参考に設定します。Javascriptと似た記法のテンプレートになります。本文マッピングテンプレートを空にした場合、Lambdaのレスポンスがそのまま通過します。
以下のマッピングテンプレートでは、Lambdaから送出されたエラーメッセージ(Exceptionのstr
)を"message"にひも付け、結果は空を返します。
{
"message" : "$input.path('$.errorMessage')",
"hogehogeResult": []
}
Lambda側のエラーをどう設計していくか
ここまでで、API GatewayとAWS Lambdaを連携させた場合のエラーの取り扱いについて、流れが分かったかと思います。
私も悩んでいますが、正規表現マッチは1つのHTTPステータスコードに対して、1行の正規表現しか記述できないため、HTTPステータスコードのひも付けという枠組の中で、どのようにエラー処理を設計するかが難しいです(もちろん|
により複数の正規表現を含めることは可能ですが、可読性は落ちます)。
コードで404や500と分かるような文字列を含めるとしても、どのレイヤーでHTTPステータスコードとひもづくエラーを送出するかの設計が難しいです。何かのモジュールを用いた場合は、そのモジュールが送出する例外をCatchして、str
を設定する必要があります。
今、開発しているAPIでは、一つ一つの例外クラスに、[404:NotFound]
や[500:InternalServerError]
といった文字列をstr
にセットしています。404系エラーのクラスを作り、子クラスで具体的なエラーを定義するのも一つの手かもしれません。
もしかすると、たくさんの例外を考慮するようなLambda関数の利用が、そもそも設計ミスかもしれません。とはいえ、手軽にサーバーレスAPIが作れるため、使わないわけにはいきません。
使いやすいAPIを目指して
使いやすく、そして分かりやすいAPIを提供する上で、適切なメッセージやエラーコードを返すことは重要です。
今回は、WebAPIのエラーについて今一度考えた後に、API GatewayとAWS Lambdaを用いた際のAPIエラーの取り扱いについて紹介しました。
次回はデプロイについて紹介します。
執筆者プロフィール
過去記事
▼第7回
「API GatewayとAWS Lambda PythonでAPI開発」Vol. 2:ローカルでの開発環境構築
▼第6回
「API GatewayとAWS Lambda PythonでAPI開発」Vol. 1:API GatewayとAWS Lambdaを知る
▼第5回
快適なシェル環境の再構築を自動化する
▼第4回
第16回情報科学技術フォーラム(FIT2017)で登壇
▼第2回
R&D論文読み会勉強会
▼第1回
言語処理100本ノック勉強会
text:DSOC R&Dグループ 高橋寛治