ローカルPC上で動作しているAzure FunctionsでAlexaからのリクエストを受け取る

#はじめに
[前回](https://qiita.com/shuhey/items/91cbd0c24d951f8a8bc2)、PC上で動かしているAzure Functionsをngrokで外部に公開してアクセスできるようにしました。

今回は、そのFunctionにAlexaスキルからリクエストを送り、Functionでそのリクエストを解析します。

#Alexaスキルのエンドポイントを設定
このFunctionsのURLはngrokを起動したときに表示される「`https://4743e56d.ngrok.io/api/Function1`」のようなURLです。

このURLをコピーして、対話モデルのほうでエンドポイントとして設定します。
ここで使う対話モデルは、[以前](https://qiita.com/shuhey/items/ce7c60c59ba25869fb94)の記事で説明のまま作ったTelloを操作するスキルの対話モデルです。

この対話モデルのエンドポイントの設定のページを開きます。
そうしたら以下のように、「HTTPS」を選択し、「デフォルトの地域」の欄に先程のFunctionのURLを入力し、その下のプルダウン項目では「開発用のエンドポイントは、証明機関が発行したワイルドカード証明書を持つ…」を選択します。
できたら「エンドポイントを保存」ボタンを押します。
これでエンドポイントの設定は完了です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/b0a17ace-34a6-3777-4c7a-52c0bd33260b.png)

では、Alexaスキルのリクエストがエンドポイントとして設定されたこのFunctionに送られるかどうか、を確認してみましょう。

Visual StudioでFunctionの処理の途中のどこか一行にブレークポイントを設定しておいてください。
そして、対話モデルのほうの「テスト」タブからスキルを起動してみます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/04f29aae-1210-5b22-97a1-820f821f97ba.png)

すると、Visual Studio側でブレークポイントで処理が一時停止する様子をみることができます。
AlexaからのリクエストがこのFunctionに送られ、処理が始まりました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/ea3c2d41-4cd9-5b3f-e36b-3a4bf21fbfe5.png)


#リクエストの解析
では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メソッドの雛形コードをすべて削除します。
自動生成された雛形は今回は全く使わないので削除します。

```csharp
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            //削除しました。
        }
```

returnも消したので、この時点ではエラーが出ます。

##Alexa.NETをインストールします

以前に、AlexaスキルをC#で作ってデプロイする[記事](https://qiita.com/shuhey/items/30ade9a86262450feeee)を書きましたが、リクエストとレスポンスを便利に扱えるライブラリである`Alexa.NET`をインストールします。

Alexaに返すのはSkillResponseクラスのオブジェクト、と決まっていますので、以下のように返り値の型を`SkillResponse`クラスにします。

```csharp
        [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の中身を文字列として読み込んでみます。

```csharp
        [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形式の文字列が格納されているのが確認できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/521a93b1-0895-124b-1db5-8530aef8b401.png)


##JSON文字列をSkillRequestにデシリアライズ
文字列として取得できさえすれば、SkillRequestクラスにデシリアライズして、あとは以前にC#でAlexaスキルを作ったときと同じ方法でSkillResponseを組み立ててreturnするだけです。

JSON文字列のデシリアライズは`NewonSoft.Json.JsonConvert.DeserializeObject`メソッドを使って以下のように書けます。


```csharp

SkillRequest skillRequest = NewtonSoft.Json.JsonConvert.DeserializeObject<SkillRequest>(jsonRequest);

```


##SkillRequestの中身を取得してみる
SkillRequestの中にはインテントの種類や名前、スロットなどいろいろな情報が入っています。
今回は簡単に、リクエストの種類でそれぞれ異なる応答を返すようにしました。

まずはソースコードからです。
ソースコード全体は以下のとおりです。

```csharp
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スキルのエミュレータからテストしてみます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/2392a59f-3160-3372-9204-7f42a1e342df.png)

どうやらリクエストの中身を取得できているようですね。

それでは今回はここまでです。

次回はNode.js版のTello操作スキルをC#で書き直します。