Microsoft Bot:如何在表单流中捕获太多尝试?

时间:2017-05-09 13:51:53

标签: c# botframework

我开发了一个BOT应用程序,我使用表单流向用户提出一组问题。现在我需要在用户输入无效选项超过3次时捕获事件。我找到了PromptDialog的TooManyAttemptsException类,但找不到FormDialog的相同内容。有没有办法捕获太多的尝试并阻止用户在FormDialog中进一步尝试?

//示例代码

{
    var enrollmentForm = new FormDialog<AppointmentQuery>(new Models.AppointmentQuery(), AppointmentForm.BuildForm, FormOptions.PromptInStart);
                        context.Call<AppointmentQuery>(enrollmentForm, this.ResumeAfterOptionDialog);

    private async Task ResumeAfterOptionDialog(IDialogContext context, IAwaitable<AppointmentQuery> result)
    {
        try
        {
            var message = await result;
        }
        catch (FormCanceledException e)
        {
            string reply = string.Empty;
            await this.StartOverAsync(context, reply);
        }
        catch (TooManyAttemptsException et)
        {
            await context.PostAsync($"Ooops! Too many attemps :(. But don't worry, I'm handling that exception and you can try again!");
            await this.StartOverAsync(context, "");
        }

        catch (Exception ex)
        {
            await context.PostAsync($"Failed with message: {ex.Message}");
        }
        finally
        {
            context.Wait(this.MessageReceivedAsync);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

FormFlow不会抛出TooManyAttemptsException。但是,解决方法是执行您自己的字段验证,如下所示:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.Bot.Builder.FormFlow;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow.Advanced;
using System.Threading.Tasks;

namespace BugFormFlowBot2
{
    [Serializable]
    [Template(TemplateUsage.EnumSelectOne, "Which {&} were you working with? {||}")]
    [Template(TemplateUsage.EnumSelectMany, "Which {&} were you working with? {||}", ChoiceStyle = ChoiceStyleOptions.PerLine)]
    public class BugReport
    {
        public string Product { get; set; }

        public string Version { get; set; }

        public List<PlatformOptions> Platform { get; set; }

        [Prompt("What is the {&}")]
        [Describe("Description of the Problem")]
        public string ProblemDescription { get; set; }

        [Numeric(1, 3)]
        public int Priority { get; set; }

        private static int versionAttempts = 0;

        public static IForm<BugReport> BuildForm()
        {
            return new FormBuilder<BugReport>()
                    .Message("Welcome to Bug Report bot!")
                    .Field(new FieldReflector<BugReport>(nameof(Product))
                            .SetType(null)
                            .SetDefine((state, field) =>
                            {
                                foreach (var prod in GetProducts())
                                    field
                                        .AddDescription(prod, prod)
                                        .AddTerms(prod, prod);

                                return Task.FromResult(true);
                            }))
                    .Field(nameof(Version),
                        validate: async (state, response) =>
                        {
                            var result = new ValidateResult { IsValid = true, Value = response };

                            foreach (var segment in (response as string ?? "").Split('.'))
                            {
                                int digit;
                                if (!int.TryParse(segment, out digit))
                                {
                                    result.Feedback =
                                        "Version number must be numeric segments, optionally separated by dots. e.g. 7.2, 10, or 3.56";
                                    result.IsValid = false;

                                    if (++versionAttempts > 2)
                                        throw new TooManyAttemptsException("Too many attempts at the version number.");

                                    break;
                                }
                            }

                            return await Task.FromResult(result);
                        })
                    .Field(nameof(Platform))
                    .AddRemainingFields()
                    .Confirm(async (bugReport) =>
                     {
                         var response = new PromptAttribute(
                             $"You entered {bugReport.Product}, {bugReport.Version}, {bugReport.Platform}" +
                             $"{bugReport.ProblemDescription}, {bugReport.Priority}. Is this Correct?");
                         return await Task.FromResult(response);
                     })
                    .OnCompletion(async (context, bugReport) => 
                     {
                        await context.PostAsync("Thanks for the report!");
                     })
                    .Build();
        }

        static List<string> GetProducts()
        {
            return new List<string>
            {
                "Office",
                "SQL Server",
                "Visual Studio"
            };
        }
    }
}

注意versionAttempts字段是private static的方式。然后查看Version字段的验证以及它如何抛出TooManyAttemptsException

这并不能完全解决问题,因为FormFlow会在FormCanceledException中包装所有异常。以下代码显示了如何处理它。

using System;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow;
using System.Net.Http;
using System.Net;

namespace BugFormFlowBot2
{
    [BotAuthentication]
    public class MessagesController : ApiController
    {
        internal static IDialog<BugReport> MakeRootDialog()
        {
            return Chain.From(() => FormDialog.FromForm(BugReport.BuildForm))
                        .Loop();
        }

        public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
        {
            if (activity?.Type == ActivityTypes.Message)
                try
                {
                    await Conversation.SendAsync(activity, MakeRootDialog);
                }
                catch (FormCanceledException fcEx) when(fcEx.InnerException is TooManyAttemptsException)
                {
                    ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));

                    Activity reply = activity.CreateReply(
                        $"Too Many Attempts at {fcEx.Last}. " +
                        $"Completed Steps: {string.Join(", ", fcEx.Completed)}");

                    await connector.Conversations.ReplyToActivityAsync(reply);
                }
                catch (FormCanceledException fcEx)
                {
                    ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));

                    Activity reply = activity.CreateReply(
                        $"Form cancelled at {fcEx.Last}. " +
                        $"Completed Steps: {string.Join(", ", fcEx.Completed)}");

                    await connector.Conversations.ReplyToActivityAsync(reply);
                }

            return Request.CreateResponse(HttpStatusCode.OK);
        }
    }
}

使用FormCanceledException注意Post中的when(fcEx.InnerException is TooManyAttemptsException)处理程序,以便了解TooManyAttemptsException何时发生。有趣的是,FormCanceledException提供了有关已完成字段的更多信息,为您提供有关异常时表单状态的更多信息。

您可以在我的BotDemos GitHub存储库中的BugFormFlowBot2项目中找到此代码。