はじめに
前回、PC上で動かしているAzure Functionsをngrokで外部に公開してアクセスできるようにしました。
今回は、そのFunctionにAlexaスキルからリクエストを送り、Functionでそのリクエストを解析します。
Alexaスキルのエンドポイントを設定
このFunctionsのURLはngrokを起動したときに表示される「https://4743e56d.ngrok.io/api/Function1
」のようなURLです。
このURLをコピーして、対話モデルのほうでエンドポイントとして設定します。
ここで使う対話モデルは、以前の記事で説明のまま作ったTelloを操作するスキルの対話モデルです。
この対話モデルのエンドポイントの設定のページを開きます。
そうしたら以下のように、「HTTPS」を選択し、「デフォルトの地域」の欄に先程のFunctionのURLを入力し、その下のプルダウン項目では「開発用のエンドポイントは、証明機関が発行したワイルドカード証明書を持つ…」を選択します。
できたら「エンドポイントを保存」ボタンを押します。
これでエンドポイントの設定は完了です。
では、Alexaスキルのリクエストがエンドポイントとして設定されたこのFunctionに送られるかどうか、を確認してみましょう。
Visual StudioでFunctionの処理の途中のどこか一行にブレークポイントを設定しておいてください。
そして、対話モデルのほうの「テスト」タブからスキルを起動してみます。
すると、Visual Studio側でブレークポイントで処理が一時停止する様子をみることができます。
AlexaからのリクエストがこのFunctionに送られ、処理が始まりました。
リクエストの解析
ではAlexaからのリクエストの中身はどうやって取得したら良いでしょうか。
上で、Alexaからのリクエストが渡されたときに実行されるメソッドはRun
メソッドであることがわかりました。
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
では、メソッドの引数にAlexaからのリクエストのデータがあるはずです。
また、それはJSON形式の文字列のはずです。これまでどおり。
さて、Azure Functionsでは、呼び出し側から送られるデータ諸々はHttpRequest
型のreq
引数に格納されます。
このreq
からリクエストの中身を取得できるはずです。
では順にやっていきましょう。
まずはRunメソッドの雛形コードをすべて削除します。
自動生成された雛形は今回は全く使わないので削除します。
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
//削除しました。
}
returnも消したので、この時点ではエラーが出ます。
Alexa.NETをインストールします
以前に、AlexaスキルをC#で作ってデプロイする記事を書きましたが、リクエストとレスポンスを便利に扱えるライブラリであるAlexa.NET
をインストールします。
Alexaに返すのはSkillResponseクラスのオブジェクト、と決まっていますので、以下のように返り値の型をSkillResponse
クラスにします。
[FunctionName("Function1")]
public static async Task<SkillResponse> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
//とりあえずreturnしておくことにした
return new SkillResponse();
}
JSON文字列のリクエストを取得する
さてそれではリクエストの中身を取得してみましょう。
HttpRequestクラスはBody
プロパティを持っていて、このBody
プロパティはStreamクラスのオブジェクトです。
Streamクラスということは、StreamReaderからBodyプロパティの中身を文字列として読み込めそうです。
では以下のようにStreamReaderを使ってBodyの中身を文字列として読み込んでみます。
[FunctionName("Function1")]
public static async Task<SkillResponse> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
using (var sr = new StreamReader(req.Body))
{
var requestJson = await sr.ReadToEndAsync();
}
//とりあえずreturnしておくことにした
return new SkillResponse();
}
ブレークポイントで止めて変数requestJson
の中身を見てみると、JSON形式の文字列が格納されているのが確認できます。
JSON文字列をSkillRequestにデシリアライズ
文字列として取得できさえすれば、SkillRequestクラスにデシリアライズして、あとは以前にC#でAlexaスキルを作ったときと同じ方法でSkillResponseを組み立ててreturnするだけです。
JSON文字列のデシリアライズはNewonSoft.Json.JsonConvert.DeserializeObject
メソッドを使って以下のように書けます。
SkillRequest skillRequest = NewtonSoft.Json.JsonConvert.DeserializeObject<SkillRequest>(jsonRequest);
SkillRequestの中身を取得してみる
SkillRequestの中にはインテントの種類や名前、スロットなどいろいろな情報が入っています。
今回は簡単に、リクエストの種類でそれぞれ異なる応答を返すようにしました。
まずはソースコードからです。
ソースコード全体は以下のとおりです。
using System;
using System.IO;
using System.Threading.Tasks;
using Alexa.NET.Request;
using Alexa.NET.Response;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace TelloControlCSharpSkill
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<SkillResponse> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
string requestJson = "";
using (var sr = new StreamReader(req.Body))
{
requestJson = await sr.ReadToEndAsync();
}
SkillRequest skillRequest = JsonConvert.DeserializeObject<SkillRequest>(requestJson);
var skillResponse=new SkillResponse
{
Version = "1.0",//お約束
Response = new ResponseBody()
};
//テスト用
if (skillRequest.Request.Type == "LaunchRequest")
{
skillResponse.Response.OutputSpeech=new PlainTextOutputSpeech
{
Text = "ローンチリクエストです。"
};
}
else if (skillRequest.Request.Type == "IntentRequest")
{
skillResponse.Response.OutputSpeech=new PlainTextOutputSpeech
{
Text = "インテントリクエストです。"
};
}
else
{
skillResponse.Response.OutputSpeech = new PlainTextOutputSpeech
{
Text = "それ以外です。"
};
}
//とりあえずreturnしておくことにした
return skillResponse;
}
}
}
Alexaスキルのエミュレータからテストしてみます。
どうやらリクエストの中身を取得できているようですね。
それでは今回はここまでです。
次回はNode.js版のTello操作スキルをC#で書き直します。