我已经将Chat Bot部署到了窗口服务器中,但是几天之后,我在Bot中收到以下错误消息:“将此消息发送到您的机器人时出错:HTTP状态代码InternalServerError”,并且应用程序洞察显示了以下消息:“操作返回无效状态代码“未经授权”,并且在过去4天中发生了8次。
这是什么意思:
1)邮件无法到达窗口服务器。
2)消息已到达服务器,并且聊天机器人出现问题。
3)这是微软方面的问题
我正在使用3.15.3版本(Bot.builder)。
有人遇到同样的问题吗?
答案 0 :(得分:1)
不幸的是,这似乎是一个issue in the Bot Framework itself,当前解决此问题的方法是在您DocumentDbBotDataStore
中注册一个TableBotDataStore
而不是Global.asax.cs_ApplicationStart
,将它们放在:
var uri = new Uri(ConfigurationManager.AppSettings["DocumentDBUri"]);
var key = ConfigurationManager.AppSettings["DocumentDBKey"];
var store = new DocumentDbBotDataStore(uri, key);
Conversation.UpdateContainer(
builder =>
{
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
});
编辑:
有一个我不想发布的巨大胶带修复,我已经实现了一段时间,并设法使其正常工作。这个想法是从根本上定位制造麻烦的服务,并通过覆盖它并强制其重试来破坏bot框架。机器人框架的令牌缓存管理器有时会出现故障,并且在发出请求之前我们不会及时刷新访问令牌,因为我们导致了Unauthorized Error
,因此我们也对其进行了破解并强制其刷新令牌。>
方法如下:
我们重写了机器人框架的IBotToUser
装饰器,该装饰器使HttpRequest
引起了错误:
public class RetryHandlerDecorator : IBotToUser
{
private readonly IMessageActivity _toBot;
private readonly IConnectorClient _client;
public RetryHandlerDecorator(IMessageActivity toBot, IConnectorClient client)
{
SetField.NotNull(out _toBot, nameof(toBot), toBot);
SetField.NotNull(out _client, nameof(client), client);
}
IMessageActivity IBotToUser.MakeMessage()
{
var toBotActivity = (Activity)_toBot;
return toBotActivity.CreateReply();
}
async Task IBotToUser.PostAsync(IMessageActivity message, CancellationToken cancellationToken)
{
try
{
await _client.Conversations.ReplyToActivityAsync((Activity)message, cancellationToken);
}
catch (Exception e)
{
if (IsTransientError(e))
{
await HandleRetry(message, cancellationToken);
}
else
{
throw;
}
}
}
private async Task HandleRetry(IMessageActivity activity, CancellationToken token)
{
await ForceRefreshTokenAsync();
await _client.Conversations.ReplyToActivityAsync((Activity)activity, token);
}
private async Task ForceRefreshTokenAsync()
{
var credentialsManager = new MicrosoftAppCredentials(
ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppIdKey],
ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppPasswordKey]);
// force the generation of a new token
// this will store the token in a static cache list, so no harm in creating a new instance of MicrosoftAppCredentials
await credentialsManager.GetTokenAsync(true);
}
private static bool IsTransientError(Exception e)
{
switch (e)
{
case ErrorResponseException ex:
return ex.Response.StatusCode == HttpStatusCode.Unauthorized;
default:
return false;
}
}
}
我们重写了注册IBotToUser
服务的bot框架模块:
public class BotToUserModuleOverride : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<RetryHandlerDecorator>().Keyed<IBotToUser>(typeof(RetryHandlerDecorator))
.InstancePerLifetimeScope();
RegisterAdapterChain<IBotToUser>(builder,
typeof(RetryHandlerDecorator), // this was previously AlwaysSendDirect_BotToUser
typeof(AutoInputHint_BotToUser),
typeof(MapToChannelData_BotToUser),
typeof(LogBotToUser)
)
.InstancePerLifetimeScope();
}
public static IRegistrationBuilder<TLimit, SimpleActivatorData, SingleRegistrationStyle> RegisterAdapterChain<TLimit>(ContainerBuilder builder, params Type[] types)
{
return
builder
.Register(c =>
{
var service = default(TLimit);
return types.Aggregate(service,
(current, t) => c.ResolveKeyed<TLimit>(t, TypedParameter.From(current)));
})
.As<TLimit>();
}
}
最后,我们通过注册覆盖服务的模块 hack bot框架的IoC容器,这需要在Global.asax.cs ApplicationStart
中完成:
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new BotToUserModuleOverride());
});
因此,将发生的情况是,每当UnauthorizedError
方法引发ReplyToActivityAsync
时,就会有一个强制刷新访问令牌并重试的过程。