C# 客户端和服务器位于同一web应用程序中的Azure Bot项目

C# 客户端和服务器位于同一web应用程序中的Azure Bot项目,c#,asp.net-mvc,.net-core,azure-active-directory,botframework,C#,Asp.net Mvc,.net Core,Azure Active Directory,Botframework,我已经使用BotFrameworkV4创建了一个Azure机器人应用程序,并使用作为接口。我注意到bot服务器的dotnetcore应用程序有一个wwwroot文件夹,其中有一个占位符HTML页面,因此我认为在那里托管webchat客户端可能比较方便。但现在,我的webchat客户端使用DirectLine将活动发送回为其提供服务的同一后端,这似乎与直觉背道而驰 我选择webchat客户端是因为我需要定制客户端的外观。我还需要为bot客户端提供服务的MVC应用程序包含azureactivedir

我已经使用BotFrameworkV4创建了一个Azure机器人应用程序,并使用作为接口。我注意到bot服务器的dotnetcore应用程序有一个wwwroot文件夹,其中有一个占位符HTML页面,因此我认为在那里托管webchat客户端可能比较方便。但现在,我的webchat客户端使用DirectLine将活动发送回为其提供服务的同一后端,这似乎与直觉背道而驰

我选择webchat客户端是因为我需要定制客户端的外观。我还需要为bot客户端提供服务的MVC应用程序包含azureactivedirectoryb2c身份验证(确实如此)。用户应该在身份验证之前和之后看到webchat客户端,但是bot后端(处理活动)需要知道用户是否登录并相应地修改其行为(我正在使用DirectLine努力实现这一点)

因此,我的第一个问题(关于StackOverflow)是:由于Bot后端和webchat客户端前端托管在同一个单一的Azure web应用程序中,是否有必要使用DirectLine,还是有更简单的方法

my 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
}
    public void ConfigureServices(IServiceCollection services)
    {
同样在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
}
    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.");
            };
        });

    }
services.AddAuthentication(sharedOptions=>
{
sharedOptions.DefaultScheme=CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme=OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAdB2C(选项=>Configuration.Bind(“身份验证:AzureAdB2C”,选项))
.AddCookie();
services.AddMvc();
services.AddSession(选项=>
{
options.IdleTimeout=TimeSpan.FromHours(1);
options.CookieHttpOnly=true;
});
//创建并添加会话状态。
var conversationState=新的conversationState(数据存储);
服务。AddSingleton(会话状态);
var userState=新的userState(数据存储);
services.AddSingleton(userState);
services.AddBot(选项=>
{
options.CredentialProvider=新的SimpleCredentialProvider(endpointService.AppId,endpointService.AppPassword);
options.ChannelProvider=新配置ChannelProvider(配置);
//捕获会话回合期间发生的任何错误,并将其记录到当前会话
//配置ILogger。
ILogger logger=_loggerFactory.CreateLogger();
options.OnTurnError=异步(上下文,异常)=>
{
logger.LogError($“捕获异常:{Exception}”);
wait context.SendActivityAsync(“抱歉,看起来好像出了问题。”);
};
});
}
我的控制器的索引方法:

    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);
    }
公共异步任务索引()
{
字符串用户标识;
if(User.Identity.IsAuthenticated)
{
字符串aadb2cUserId=User.FindFirst(“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier1.价值;
Users.EnsureAccountExists(aadb2cUserId);//确保在本地知道具有给定AAD标识符的帐户(如果不知道,则创建该帐户)
userId=$“ia_{aadb2cUserId}”;
}
其他的
{
userId=$“na_{Guid.NewGuid()}”;
}
HttpClient=新的HttpClient();
字符串directLineUrl=$”https://directline.botframework.com/v3/directline/tokens/generate";
HttpRequestMessage请求=新的HttpRequestMessage(HttpMethod.Post,directLineUrl);
//TODO:把它放在配置文件的某个地方
var secret=“*”).focus();
}

问题很长,但答案会短得多

所以我的第一个问题(关于StackOverflow)是:使用Bot后端 webchat客户端前端托管在同一个 Azure web app,是否需要使用DirectLine,或者是否存在 这样做更简单吗


是的,这是必要的。事实上,所有频道类型都使用Bot连接器与后端(您的Bot代码)通信,无法直接访问。原因有很多,其中一个是账单!

我有点不同意Nicolas R的观点。当涉及到直接访问您的机器人时,您可能想看看以下内容:


还有一个选项,我认为这可能有助于您寻求的那种直接沟通。

好的方面。但第一个是非官方的软件包,我不确定这是微软会鼓励的方向(但由于您是微软的支持团队成员,您必须了解更多这方面的信息)。我确实和你一样犹豫不决,我只是在与上级讨论后才发布了这个答案,以确保它没有问题。脱机directline软件包也是由一名Microsoft员工开发的。Kyle和Nicolas感谢您提供的信息性答案:-)我认为脱机directline选项允许与托管bot,同时通过标准通道(DL、Skype等)保持开放通信?浏览器托管选项是否也排除了bot框架,因此不允许通过其他通道(托管它的浏览器除外)与bot通信?@JohnChurch-我没有尝试脱机directline,所以我需要仔细研究一下才能回答您的问题。我想您将无法通过另一个通道连接到浏览器托管的机器人,尽管我也没有尝试过。@JohnChurch-我在本地机器上做了一些测试,下面是我的发现。这两个都是ese选项可能仍然可以公开标准通道可以访问的端点,但在身份验证方面,您可能会错过,而且听起来您希望您的bot是secur