LoginSignup
21

More than 5 years have passed since last update.

APIGateway+Lambdaでイカしたエラーレスポンスをする

Last updated at Posted at 2016-08-25

経緯

APIGateway+Lambdaでこんな感じのイカしたエラーレスポンスをしたい。
http://qiita.com/suin/items/f7ac4de914e9f3f35884#github

{
   "message": "Validation Failed",
   "errors": [
   ]
}

でも意外と苦労したのでメモ。

Lambdaのレスポンスに困った

Lambdaのcallbackで cb(err, null) をコールするとerrの型がなんであれ、

{
  "errorMessage": "何かのエラー",
  "errorType": "Error",
  "stackTrace": [
    "module.exports (/var/task/src/operation/xxxx.js:27:9)",
    "next (native)",
    "onFulfilled (/var/task/node_modules/co/index.js:65:19)",
    "process._tickDomainCallback (node.js:407:9)"
  ]
}
  • errorMessage
  • errorType
  • stackTrace

という3属性をもったオブジェクトを返します。というかこの3つの属性以外返ってきません。

なので、

class HogeError extends Error {
    constructor(msg, detail) {
       super(msg);
       this.detail = detail;
    }
}

こんな独自Errorを定義してもdetail属性は無視されてしまいます。

どうすれば良いのか

1. Lambda側(Node.js側)でJSON.stringifyする

class HogeError extends Error {
  constructor(msg, details) {
    const obj = {
      code: "Forbidden",
      message: msg,
      errors: details
    };
    super(JSON.stringify(obj));   //<---ここでmessageに文字列化されたJSONをセットする
  }
}

こうするとLambdaのレスポンスはこんな感じになります。

{
  "errorMessage": "{\"code\":\"Forbidden\",\"message\":\"hogehoge\",\"errors\":[{\"reason1\":\"hogeがあれなんでだめ\"},{\"reason2\":\"これがhogeなんでだめ\"}]}",
  "errorType": "Error",
  "stackTrace": [...]
}

2. APIGateway側でレスポンスのマッピングテンプレートを使う

マッピングテンプレート
#set ($e = $util.parseJson($input.path('$.errorMessage')))
{
  "message" : $e.message,
  "errors": $e.errors
}

このようにして、errorMessageの文字列をparseJsonし、マッピングする設定をします。
See: developerguide/api-gateway-mapping-template-reference

3. これで晴れて次のようなレスポンスを返すことができました。

ステータス403
{
   "message" : hogehoge,
   "errors": [{reason1=hogeがあれなんでだめ}, {reason2=これがhogeなんでだめ}]
}

蛇足

応答コードを決める

応答コードを出し分けるところでも若干悩んだので書いておきます。
統合レスポンスの設定で、正規表現マッチングをして応答コードを決められるのですが、これが何に対するマッチングなのかというところです。

responseintegrate.png

developerguide/how-to-method-settings-execution-consoleによると、

Note
エラーパターンは Lambda 応答の errorMessage プロパティに対してマッチングされます。これは、Node.js の場合は context.fail(errorMessage)、Java の場合は throw new MyException(errorMessage) によって設定されます。

ということで、先の例で行くと、

{
  "errorMessage": "{\"code\":\"Forbidden\",\"message\":\"hogehoge\",\"errors\":[{\"reason1\":\"hogeがあれなんでだめ\"},{\"reason2\":\"これがhogeなんでだめ\"}]}",
  "errorType": "Error",
  "stackTrace": [...]
}

errorMessageの値、つまり、{"code":"Forbidden","message":... という文字列に対するマッチングとなります。

swaggarでの定義書式

エクスポートした奴の抜粋を晒しておきます。

swagger.yamlの抜粋
x-amazon-apigateway-integration:
  responses:
    default:
      statusCode: "200"
    .*"code":"Forbidden".*:
      statusCode: "403"
      responseTemplates: 
        application/json: |
          #set ($e = $util.parseJson($input.path('$.errorMessage')))
          {
            "message" : $e.message,
            "errors": $e.errors
          }
    .*"code":"BadRequest".*:
      statusCode: "400"
      responseTemplates:
        application/json: |
          #set ($e = $util.parseJson($input.path('$.errorMessage')))
          {
            "message" : $e.message,
            "errors": $e.errors
          }

さくっと試せるサンプル

ここに置いておきます。serverless frameworkでデプロイできるようにしてあります。

$ sls deploy

が、上記の統合レスポンスの設定をserverless.ymlでどう書いて良いのやらわからず、swagger書式のモノの抜粋を示した次第であります。

(バージョンが上がって、s-function.jsonとかs-templates.jsonとかはなくなっちゃったんでしょうか??)

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
21