Я создал бот-приложение 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>