前回のブログ
今回はエラー処理について紹介します。 スキル開発中に1つ1つバグを修正していったとしても、ユーザーの操作次第で想定していない処理をしてしまうのは良くある話だと思います。 その結果、エラーが発生してしまうかもしれないので、あらゆる処理に例外処理(try〜catch〜)を入れ込んでいくのは、とても冗長です。 ASK SDK v2では例外処理を共通的に実装するすべが用意されているので紹介します。
エラー処理の作成
前回使用していたindex.jsに次のコードを追加してください。
& nbsp;
const ErrorHandler = { canHandle(handlerInput, error) { console.log('called ErrorHandler.canHandle'); return true; }, handle(handlerInput, error) { console.log('called ErrorHandler.handle'); console.log(JSON.stringify({handlerInput:handlerInput, error:error})); return handlerInput.responseBuilder .speak('エラーが発生しました') .getResponse(); } } exports.handler = Alexa.SkillBuilders.standard() .addRequestHandlers(LaunchRequestHandler, //起動リクエスト (略) .addRequestInterceptors(RequestInterceptor) //インテント処理が始まる前の処理 .addResponseInterceptors(ResponseInterceptor) //インテント処理が終わった後の処理 .addErrorHandlers(ErrorHandler) //エラー処理を追加 .lambda();
addErrorHandlers でエラーハンドラーを登録します。
さらに起動リクエストでエラーを発生させるため、LaunchRequestHandlerの中身を次のように変更してください。
const LaunchRequestHandler = { canHandle(handlerInput) { console.log('called LaunchRequestHandler.canHandle'); return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, handle(handlerInput) { console.log('called LaunchRequestHandler.handle'); hoge+1;//ここでエラー発生 return handlerInput.responseBuilder .speak('スキルを起動しました。こんにちは、と言ってください') .reprompt('こんにちは、と言ってください') .getResponse(); } };
「hoge+1;」の部分は「hoge」という、ありもしない変数を使用しているためエラーが発生します。
「アレクサ、○○を開いて」と起動リクエストを投げると次のように動きます。
起動リクエスト(LaunchRequestHandler)のcanHandle→前処理(RequestInterceptor)→handleの順に実行しますが、 handleメソッドの途中でエラーが発生します。
次にaddErrorHandlersでセットしたハンドラー(ErrorHandler)のcanHandle→handleの順番に実行されます。 try〜catch〜の"catch"のような物で、"error"オブジェクトが送られてくるため、ここで例外処理を行うことができます。
ここで注意しなくてはいけないのは"後処理(ResponseInterceptor)"が動かないということです。
共通的なレスポンスを返す処理を後処理に任せっきりだと困ったことになるので注意してください。
非同期処理でエラーが発生した場合は?
Node.jsで一番気がかりなのは非同期処理での例外処理ではないでしょうか。 非同期の場合どうなるのかも見て見ましょう。
index.jsを次のように変更してください。
const LaunchRequestHandler = { canHandle(handlerInput) { console.log('called LaunchRequestHandler.canHandle'); return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, async handle(handlerInput) { console.log('called LaunchRequestHandler.handle'); //非同期処理 await validate(); return handlerInput.responseBuilder .speak('スキルを起動しました。こんにちは、と言ってください') .reprompt('こんにちは、と言ってください') .getResponse(); } }; const validate = ()=>{ return new Promise((resolve, rejected)=>{ setTimeout(()=>{ try { console.log('called validate.setTimeout'); hoge+1;//ここでエラー発生 resolve(); } catch(e) { rejected(e); } }, 1); }); };
handleメソッドの頭に"async"が付いたのと、 validateメソッドを呼ぶ時に"await"が付いたことに注意してください。 これを付けると非同期処理を同期処理っぽく見せることができます(Lambda ではNode.js v8からの機能)。
setTimeoutで非同期処理を行ない、先ほどと同じように"hoge+1"でエラーが発生します。 実際に動かすとどうなるか見てみましょう。
(前処理と後処理については上記と同様のため省略しています。)
LaunchRequestHandlerのhandleメソッドで非同期処理のvalidateメソッドを呼び出します。 validateメソッドでは非同期処理でエラーが発生し、rejectedへ処理を丸投げします。
ここから先は最初と同じくErrorHandlerが呼ばれてエラー処理を行うことができます。
あとがき
非同期処理でtry〜catchが必要になるのは少し残念ですが、"addErrorHandlers"を使用すると共通的にエラーハンドリングができることが確認できました。 個々にハンドリングできる部分は個々で行うのがベストだと思いますが、予期せぬエラーが発生した時にも対応できるよう、とりあえず的に仕込んでおくことをオススメします。