Проект Azure Bot с клиентом и сервером в одном веб-приложении - PullRequest
2 голосов
/ 11 марта 2019

Я создал бот-приложение Azure с использованием BotFramework v4 и использовал элемент управления веб-чата в качестве интерфейса.Я заметил, что в приложении dotnetcore на бот-сервере есть папка wwwroot с HTML-страницей-заполнителем, поэтому я подумал, что было бы целесообразно разместить там клиент веб-чата.Но теперь кажется нелогичным, что мой клиент веб-чата использует DirectLine для отправки действий обратно на тот же сервер, который его обслуживал.

Я выбрал клиент веб-чата, потому что мне нужно настроить внешний вид клиента.Мне также нужно приложение MVC, которое обслуживает бот-клиент для включения аутентификации Azure Active Directory B2C (что и делает).Пользователи должны видеть клиент веб-чата до и после проверки подлинности, но бота-энтузиаста (обрабатывающего действия) необходимо знать, вошел ли пользователь в систему, и соответствующим образом изменить его поведение (и я изо всех сил пытаюсь добиться этого с помощью DirectLine).

Итак, мой первый вопрос (когда-либо касающийся StackOverflow): если серверная часть бота и клиентская часть веб-чата размещаются в одном и том же веб-приложении Azure, необходимо ли использовать DirectLine или существуетболее простой способ сделать это?

Соответствующий код в моем Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    _loggerFactory = loggerFactory;

    app.UseStaticFiles(); // to allow serving up the JS, CSS, etc., files.
    app.UseBotFramework(); // to add middleware to route webchat activity to the bot back-end code
    app.UseSession(); // to enable session state
    app.UseAuthentication(); // to enable authentication (in this case AAD B2C)
    app.UseMvcWithDefaultRoute(); // to add MVC middleware with default route
}

Также в Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {

// стандартный код для добавленияHttpContextAssessor, BotServices, BotConfigs и синглтоны хранения памяти опущены для краткости ...

        services.AddAuthentication(sharedOptions =>
        {
            sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddAzureAdB2C(options => Configuration.Bind("Authentication:AzureAdB2C", options))
        .AddCookie();

        services.AddMvc();

        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromHours(1);
            options.CookieHttpOnly = true;
        });


        // Create and add conversation state.
        var conversationState = new ConversationState(dataStore);
        services.AddSingleton(conversationState);

        var userState = new UserState(dataStore);
        services.AddSingleton(userState);

        services.AddBot<MyBot>(options =>
        {
            options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
            options.ChannelProvider = new ConfigurationChannelProvider(Configuration);

            // Catches any errors that occur during a conversation turn and logs them to currently
            // configured ILogger.
            ILogger logger = _loggerFactory.CreateLogger<RucheBot>();
            options.OnTurnError = async (context, exception) =>
            {
                logger.LogError($"Exception caught : {exception}");
                await context.SendActivityAsync("Sorry, it looks like something went wrong.");
            };
        });

    }

Метод индексации моего контроллера:

    public async Task<ActionResult> Index()
    {
        string userId;
        if (User.Identity.IsAuthenticated)
        {
            string aadb2cUserId = User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value;
            Users.EnsureAccountExists(aadb2cUserId); // ensure account with given AAD identifier is know locally (by creating it if not)
            userId = $"ia_{aadb2cUserId}";
        }
        else
        {
            userId = $"na_{Guid.NewGuid()}";
        }


        HttpClient client = new HttpClient();
        string directLineUrl = $"https://directline.botframework.com/v3/directline/tokens/generate";
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, directLineUrl);

        // TODO: put this in the config somewhere
        var secret = "<the secret code from my bot's DirectLine channel config in the Azure portal>";
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secret);
        string jsonUser = JsonConvert.SerializeObject(new { User = new { Id = userId } });
        request.Content = new StringContent(jsonUser, Encoding.UTF8, "application/json");
        var response = await client.SendAsync(request);

        string token = string.Empty;

        if (response.IsSuccessStatusCode)
        {
            var body = await response.Content.ReadAsStringAsync();
            token = JsonConvert.DeserializeObject<DirectLineToken>(body).token;
        }

        var config = new ChatConfig()
        {
            Token = token,
            UserId = userId,
        };

        return View(config);
    }

И, наконец, код в связанном представлении:

<div id="webchat"></div>
<script type="text/javascript">
...
        /// Called asynchronously during the page load
        function renderWebChat( withSound )
        {
            var webchatOptions =
            {
                directLine: window.WebChat.createDirectLine( { secret: '@Model.Token'} ),
                userID: '@Model.UserId'
            };

            if ( withSound )
            {
                webchatOptions.webSpeechPonyfillFactory = window.WebChat.createBrowserWebSpeechPonyfillFactory();
            }

            window.WebChat.renderWebChat( webchatOptions, document.getElementById( 'webchat' ) );

            document.querySelector( '#webchat > *' ).focus();
        }

</script>

Ответы [ 2 ]

1 голос
/ 12 марта 2019

Я собираюсь немного не согласиться с Николасом Р. Когда дело доходит до прямого доступа к вашему боту, вы можете взглянуть на это: https://www.npmjs.com/package/offline-directline

Существует также возможность хостинга бота в браузере , что, я думаю, может облегчить тот тип прямой связи, который вы ищете.

1 голос
/ 12 марта 2019

Длинный вопрос, но ответ будет намного короче!

Итак, мой первый вопрос (когда-либо о StackOverflow): с бэк-бенд и клиентский интерфейс веб-чата, размещенный в том же, одном Веб-приложение Azure, необходимо ли использовать DirectLine или более простой способ сделать это?

Да, это необходимо. Фактически, все типы каналов используют Bot Connector для связи с вашим бэкэндом (вашим кодом бота), прямой доступ невозможен. Причин для этого много, например, биллинг!

...