経緯
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. これで晴れて次のようなレスポンスを返すことができました。
{
"message" : hogehoge,
"errors": [{reason1=hogeがあれなんでだめ}, {reason2=これがhogeなんでだめ}]
}
蛇足
応答コードを決める
応答コードを出し分けるところでも若干悩んだので書いておきます。
統合レスポンスの設定で、正規表現マッチングをして応答コードを決められるのですが、これが何に対するマッチングなのかというところです。
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での定義書式
エクスポートした奴の抜粋を晒しておきます。
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
とかはなくなっちゃったんでしょうか??)