【问题标题】:Bot Framework returning --There was an error sending this message to your bot: HTTP status code 401: UnauthorizedBot Framework 返回 -- 向您的机器人发送此消息时出错:HTTP 状态代码 401:未经授权
【发布时间】:2020-03-25 00:35:50
【问题描述】:

我已将聊天机器人部署到窗口服务器,但从几天开始,我在 Bot 中收到以下错误“向您的机器人发送此消息时出错:HTTP 状态代码 InternalServerError”,并且应用程序洞察力显示以下消息“操作返回无效状态码“未授权”,过去 4 天内已发生 8 次。

什么意思:

1) 消息无法到达窗口服务器。

2) 消息正在到达服务器,聊天机器人出现问题。

3) 这是微软端的问题

我使用的是 3.15.3 版本(Bot.builder)。

有人遇到过同样的问题吗?

【问题讨论】:

    标签: botframework unauthorized


    【解决方案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服务的机器人框架模块:

    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>();
        }
    }
    

    最后我们破解 bot 框架的 IoC 容器,方法是注册我们覆盖服务的模块,这需要在 Global.asax.cs ApplicationStart 中完成:

    Conversation.UpdateContainer(
        builder =>
        {
            builder.RegisterModule(new BotToUserModuleOverride());
        });
    

    所以会发生什么,每当UnauthorizedErrorReplyToActivityAsync 方法引发时,都会有一个进程强制刷新访问令牌并重试。

    【讨论】:

    • 谢谢哈维尔,但我没有使用 TableBotDataStore,所以还有其他解决方法。
    • 我用 hack 更新了我的答案以使其正常工作。最好的办法是等待微软解决这个问题,但如果你很绝望,你可以使用我发布的内容
    猜你喜欢
    • 2021-01-27
    • 2021-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多