Bot Framework返回-将此消息发送到您的机器人时出错:HTTP状态码401:未经授权

时间:2018-09-24 09:39:56

标签: botframework unauthorized

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

这是什么意思:

1)邮件无法到达窗口服务器。

2)消息已到达服务器,并且聊天机器人出现问题。

3)这是微软方面的问题

我正在使用3.15.3版本(Bot.builder)。

有人遇到同样的问题吗?

1 个答案:

答案 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时,就会有一个强制刷新访问令牌并重试的过程。