Help us understand the problem. What is going on with this article?

Amazon API Gatewayで適切なHTTPステータスコードを返す方法

背景

AmazonAPIGatewayでは、デフォルトのままLambdaと接続し、以下のようにコード上ではエラーのつもりでcallbackを返しても、HTTPステータスコードは200で返ってしまいます。

handler.js
// status codeが200で返る
callback(null, {
   "status": "400",
   "message": "Bad Request"
});

これじゃあなんともかっこ悪いので、適切なHTTPステータスコードを返せるようにします。主に気を使う項目以下の3点。Node.js前提です。

  1. Lambdaでのcallback方法
  2. APIGatewayの統合レスポンス(Integration Response)
  3. メソッドレスポンス(Method Response)

大まかな流れは、「1.でエラーとしてcallbackする → 2.でcallbackされた内容を正規表現でハンドリングして3.に登録されているステータスコードに割り振る → 呼び出し元に返却」といった感じです。

Lambdaでのcallback

そもそもcallback関数は、第一引数がnullの場合は正常終了であると判断します。よって、200で返したい場合は第一引数をnull、第二引数に返却したいオブジェクトを入れます。以下のような感じです。
※ デフォルトのAPIGatewayの設定の場合の話です。

handler.js
// 200で返る
callback(null, {
   "name": "taro",
   "age": 30
});

callback関数の第一引数がnull以外の場合は、エラー終了であると判断されます。ということは以下のような感じでいいんじゃないでしょうか。

handler.js
// 500:Internal Server Errorになる
callback({
   "status": "400",
   "message": "Bad Request"
});

これではうまくいきません。500で落ちてしまいます。リファレンスを読むと、callback関数の第一引数は文字列でなければいけないようです。よって、正しくは以下です。

handler.js
// Lambdaでの正しいエラーの返し方
callback(JSON.stringify({
   "status": "400",
   "message": "Bad Request"
}));

このようにcallbackすることによって、エラーとして正しくAPIGatewayに引き渡すことができます。この際、APIGatewayには以下のように、必ずerrorMessageにcallback関数の第一引数が込められて渡ることになります。

{
  "errorMessage": "{\"status\":400,\"message\":\"Bad Request\"}"
}

APIGatewayのメソッドレスポンス

前半の順番と前後しますが、先にメソッドレスポンスの設定を行います。ここでは、予め返す可能性のあるステータスコードを登録しておきます。

レスポンスの追加から、400を追加します(ヘッダーやモデルは今回はなしで構いません)。ここの設定はこれだけです。403や500も返したい場合は、随時追加してください。
※ 私がモデルについてあまり理解できていません、また勉強しておきます。

APIGatewayの統合レスポンス

ここでは、errorMessageに込められてきた内容を正規表現でハンドリングする設定をします。

デフォルトではこんな感じじゃないでしょうか。
スクリーンショット 2016-10-05 12.53.54.png

ここに400の場合を追加していきます。統合レスポンスの追加を押すと以下のようになると思います。
スクリーンショット 2016-10-05 12.59.57.png

メソッドレスポンスのステータスは400を選択します。ここには予めメソッドレスポンスの設定で追加しておかないと表示されませんのでご注意ください。

正規表現ですが、ここはLambdaでのcallbackの内容と関連してきます。今回は"status":400の部分でハンドリングを行うことにします。正規表現の欄には.*"statusCode" *: *400.*と入力します。ちなみにこれをこちらのサイトを使って図示するとこんな感じです。念のためコロン周りのスペースにも対応できるようにしておきました。まだ保存は押さないでくださいね。
スクリーンショット 2016-10-05 13.05.01.png

次に本文マッピングテンプレートの設定をします。マッピングテンプレートの追加から、application/jsonとしてください。

errorMessageに入れておいた内容を取得して、中身を表示するには次のようにします。

#set($errorObj = $util.parseJson($input.path('$.errorMessage')))
{
    "error" : {
        "message" : "$errorObj.message"
    }
}

この部分は勉強不足なので、現在わかっている範囲の話ですが、APIGatewayに渡されたオブジェクトは、$input.path('$')で取得できます。今回はerrorMessageに入れられてくるので$.errorMessageとしています。ただ、取得した内容はあくまでString(LambdaでJSON.stringifyにして渡しているため)ですので、$util.parseJson()関数を使ってStringパースしています。
表示させたい構造を書いて、変数部分は$errorObjを通して呼び出してあげれば、レスポンスの内容を自由に変更することができます。

このようにすると、適切なHTTPステータスコードとともに返したい内容をかえ返せるようになりました!

まとめ

個人で使っている分にはすべて200で返ってきていても特に問題がなかったのですが、業務ではそうはいかないので今回でそこそこ理解できて良かったです。この部分は英語の記事はありましたが、日本語の記事はあまりなかったように感じたので記事にしました。誰かの参考になれば幸いです。

何か間違っている点や不明な点がありましたらコメントをお願いいたします。

おわり

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした