Bot Builder v4 でボット開発 : ダイアログにおけるリトライの処理と値の検証

これまでの記事ではダイアログの基本的な使い方を見てきましたが、今回はダイアログにおけるリトライの処理と値の検証を見ていきます。

# ダイアログのリトライと検証

既に見てきたように、プロンプトを使えばユーザーインプットの型を確認して、期待した型でない場合は自動でリトライしてくれますが、ダイアログにはリトライ時の処理を変えたり、型だけでなくより詳細な検証を行える機能があります。

# リトライの処理

これまでの記事では、プロンプトオプションを使って、ユーザーに対して表示する文字列や選択肢を設定しました。

```csharp
return await stepContext.PromptAsync(
    "choice",
    new PromptOptions
    {
        Prompt = MessageFactory.Text("今日はなにをしますか?"),
        Choices = choices,
    },
    cancellationToken);
```

プロンプトオプションでは上記以外に、リトライ時の処理と検証で使えるプロパティを設定することが可能です。検証のプロパティについては後述しますが、ここではリトライの処理を確認します。

## リトライ時のプロンプト

ProfileDialog の年齢を聞くプロンプトで、リトライを実装してみます。

1\. ProfileDialog.cs の AgeStepAsync を以下のように変更して RetryPrompt を追加。

```csharp
private async Task<DialogTurnResult> AgeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Result より結果の確認
    if ((bool)stepContext.Result)
        // 年齢を聞いてもいい場合は "age" ダイアログを送信
        return await stepContext.PromptAsync(
            "age",
            new PromptOptions
            {
                Prompt = MessageFactory.Text("年齢を入力してください。"),
                RetryPrompt = MessageFactory.Text("0-120 の数字で入力してください。"),
            },
            cancellationToken);
    else
        // "いいえ" を選択した場合、次のステップに進む。"age" ダイアログの結果は "-1" を指定。
        return await stepContext.NextAsync(-1, cancellationToken);
}
```

2\. F5 を押下してデバッグ実行。年齢の確認時に文字列を送信。リトライ時に指定したプロンプトが返ることを確認。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/50c18402-e76e-ec0d-967f-1cde63a082c6.png)

# プロンプト単位での検証

年齢確認で利用している NumberPrompt では、応答の値が数値であるかの検証は行われますが、値の範囲などは検証できません。よって 300 歳など期待していない値も指定できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/2b71a96a-e080-75e3-1a6a-a327640129b3.png)

## 検証用のメソッドの実装と利用

型より詳細な検証が必要な場合は、プロンプトに検証用のメソッドを定義します。

1\. Validators フォルダを作成して、NumberValidators.cs ファイルを追加。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/ab92c26f-8389-7d42-61aa-c6d5fdfc1ce4.png)

2\. コードを以下のものと差し替え。

```csharp

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;

public static class NumberValidators
{
    public static Task<bool> ValidateRangeAsync(PromptValidatorContext<int> promptContext, CancellationToken cancellationToken)
    {
        // 期待した型であったか確認して違えば失敗とみなす
        if(!promptContext.Recognized.Succeeded)
        {
            return Task.FromResult(false);
        }
        // ユーザーのインプットを取得
        var value = promptContext.Recognized.Value;

        // 0 より小さいか 120 より大きい場合は False
        if (value < 0 || value > 120)
        {
            return Task.FromResult(false);
        }

        return Task.FromResult(true);
    }
}
```

3\. ProfileDialog.cs のコンストラクタで、作成した検証用のメソッドを指定。

```csharp
AddDialog(new NumberPrompt<int>("age", NumberValidators.ValidateRangeAsync));
```

4\. age プロンプトでリトライを設定。

```csharp
// 年齢を聞いてもいい場合は "age" ダイアログを送信
return await stepContext.PromptAsync(
    "age", 
    new PromptOptions
    {
        Prompt = MessageFactory.Text("年齢を入力してください。"),
        RetryPrompt = MessageFactory.Text("0-120 の数字で入力してください。"),
    },
    cancellationToken);
````

5\. F5 キーを押下してデバッグ実行。0 未満または 120 より大きい数字を入力。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/b9c7f8d3-d98f-142e-f2eb-49ce76bb425d.png)

## プロンプトオプションの検証プロパティ利用

実装した検証用プロパティでは、常に決まった範囲しか検証できない課題があり、値を変える度に新しい検証用メソッドが必要となります。そこでプロンプトオプションから動的にパラメーターを渡せるように変更します。

1\. Models フォルダに NumberRange.cs を追加。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/a59d7739-93c5-c67f-aba4-99f0b5f67f90.png)

2\. 以下のコードと差し替え。

```csharp
public class NumberRange
{
    public int MinValue { get; set; }
    public int MaxValue { get; set; }
}
```

3\. NumberValidators.cs の ValidateRangeAsync メソッドを以下の様に変更。

```csharp
public static Task<bool> ValidateRangeAsync(PromptValidatorContext<int> promptContext, CancellationToken cancellationToken)
{
    // 期待した型であったか確認して違えば失敗とみなす
    if(!promptContext.Recognized.Succeeded)
    {
        return Task.FromResult(false);
    }
    
    // ユーザーのインプットを取得
    var value = promptContext.Recognized.Value;

    // オプションの検証プロパティから NumberRange を取得。
    NumberRange range = promptContext.Options.Validations is NumberRange ?
        (NumberRange)promptContext.Options.Validations :
        new NumberRange() { MinValue = 0, MaxValue = 120 };

    // MinValue より小さいか MaxValue より大きい場合は False
    if (value < range.MinValue || value > range.MaxValue)
    {
        return Task.FromResult(false);
    }

    return Task.FromResult(true);
}
```

4\. ProfileDialog.cs の age プロンプトでオプションを設定。

```csharp
var numberRange = new NumberRange() { MinValue = 0, MaxValue = 120 };
return await stepContext.PromptAsync(
    "age",
    new PromptOptions
    {
        Prompt = MessageFactory.Text("年齢を入力してください。"),
        RetryPrompt = MessageFactory.Text($"{numberRange.MinValue}-{numberRange.MaxValue} の数字で入力してください。"),
    // 検証プロパティに NumberRange を設定
    Validations = numberRange,
    },
    cancellationToken);
```

5\. F5 キーを押下してデバッグ実行。動作を確認。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214116/09a3c464-16a4-feea-5d87-6c7294d19e76.png)

# まとめ

ボットアプリケーションではユーザーが自由にデータを送信できるため、開発者が意図しないデータが送られてくることがあります。検証やリトライを使えば、正しいデータを効率よく収集できるようになります。

次回はテキストや選択肢の他に、ダイアログでどのようなメッセージが送れるか見ていきます。

[次の記事へ](https://qiita.com/kenakamu/items/eb3e7db2141525bd95ef)
[目次へ戻る](https://qiita.com/kenakamu/items/6dc043cfc1f199032883)

[この記事のサンプルコード](https://github.com/kenakamu/botbuilderv4completeguide/tree/article11)