【问题标题】:Calling ApiController causes SignalR to stop working调用 ApiController 会导致 SignalR 停止工作
【发布时间】:2017-03-22 06:28:23
【问题描述】:

我有一个使用 SignalR (2.2.1) 和 Azure 移动应用进行身份验证的监控/日志记录中心。无论 ApiController 上是否存在 MobileAppController 属性,即使 ApiController 不包含集线器调用代码,从浏览器调用 /api/values 也会立即终止我的 SignalR 集线器。我必须重新启动网络应用程序才能让它再次工作。同时,网页将继续尝试连接/重新连接。

条带化的控制器看起来像

public class ValuesController : ApiController
{
    // GET api/values
    public string Get()
    {
        return "Hello World!";
    }
}

我在里面设置了WebApi

    public static void ConfigureMobileApp(IAppBuilder app, IDependencyResolver resolver)
    {
        HttpConfiguration config = new HttpConfiguration();
        config.DependencyResolver = resolver;
        config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
        config.Formatters.Add(new BsonMediaTypeFormatter());

        new MobileAppConfiguration()
            //.MapApiControllers()
            .AddTables(
                new MobileAppTableConfiguration()
                    .MapTableControllers()
                    .AddEntityFramework()
            )
            .AddPushNotifications()
            .MapLegacyCrossDomainController()
            .ApplyTo(config);

        // Use Entity Framework Code First to create database tables based on your DbContext
        Database.SetInitializer(new MobileServiceInitializer());

        MobileAppSettingsDictionary settings = config.GetMobileAppSettingsProvider().GetMobileAppSettings();

        if (string.IsNullOrEmpty(settings.HostName))
        {
            app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
            {
                // This middleware is intended to be used locally for debugging. By default, HostName will
                // only have a value when running in an App Service application.
                SigningKey = ConfigurationManager.AppSettings["SigningKey"],
                ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
                ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
                TokenHandler = config.GetAppServiceTokenHandler()
            });
        }

        config.Routes.MapHttpRoute(
              name: "DefaultApi",
              routeTemplate: "api/{controller}/{id}",
              defaults: new { id = RouteParameter.Optional }
          );
        app.UseWebApi(config);
    }

SignalR 设置使用

    public static void ConfigureSignalR(IAppBuilder app, CustomNinjectDependencyResolver resolver)
    {           
        IKernel kernel = resolver.Kernel;
        var connectionManager = resolver.Resolve<IConnectionManager>();
        var heartbeat = resolver.Resolve<ITransportHeartbeat>();
        var hubPipeline = resolver.Resolve<IHubPipeline>();

        kernel.Bind<IConnectionManager>().ToConstant(connectionManager);
        kernel.Bind<IUserIdProvider>().To<ZumoUserIdProvider>().InSingletonScope();

        var hubConfig = new HubConfiguration
        {
            Resolver = resolver,
            EnableDetailedErrors = true,
            EnableJSONP = true,
        };

        hubPipeline.AddModule(kernel.Get<LoggingHubPipelineModule>());

        app.MapSignalR(hubConfig);
    }

当我尝试转到 /api/values 时,会发生这种情况

Microsoft.Azure.AppService.Authentication Verbose: 0 : Received request: GET https://kamelos-app.azurewebsites.net/api/values
Microsoft.Azure.AppService.Authentication Verbose: 0 : Found 'AppServiceAuthSession' cookie for site 'xxx.azurewebsites.net'. Length: 768.
Microsoft.Azure.AppService.Authentication Verbose: 0 : Authenticated xxx successfully using 'Session Cookie' authentication.
Exception thrown: 'System.Reflection.ReflectionTypeLoadException' in mscorlib.dll
Exception thrown: 'System.Net.WebSockets.WebSocketException' in System.Web.dll
Exception thrown: 'System.Net.WebSockets.WebSocketException' in mscorlib.dll
Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
Exception thrown: 'System.OperationCanceledException' in System.Web.dll
Microsoft.Azure.AppService.Authentication Verbose: 0 : Received request: GET https://kamelos-app.azurewebsites.net/favicon.ico
Microsoft.Azure.AppService.Authentication Verbose: 0 : Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
Found 'AppServiceAuthSession' cookie for site 'kamelos-app.azurewebsites.net'. Length: 768.
Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
Microsoft.Azure.AppService.Authentication Verbose: 0 : Authenticated xxx successfully using 'Session Cookie' authentication.
Exception thrown: 'System.OperationCanceledException' in mscorlib.dll

尝试将 SignalR 降级到 2.1.2 无济于事。同样的问题。

更新。如果 SignalR 会话未在进行中,并且我转到 /api/values,则输出日志中有 WebSocketException,但是,之后转到监控页面,那里不再是 SignalR。至于监控代码:

<script type="text/javascript">
    $(function () {
        function encodeHtml(html) {
            // html still emits double quotes so we need to replace these entities to use them in attributes.
            return $("<div/>").text(html).html().replace(/\"/g, "&quot;");
        }
        var monitor = $.connection.monitor;
        $.connection.hub.logging = true;
        monitor.client.logError = function (value) {
            $('#logs').prepend('<li class="error">' + encodeHtml(value).replace('\n', '<br/>') + '</li>');
        };
        monitor.client.logWarn = function (value) {
            $('#logs').prepend('<li class="warn">' + encodeHtml(value).replace('\n', '<br/>') + '</li>');
        };
        monitor.client.logMessage = function (value) {
            $('#logs').prepend('<li class="message">' + encodeHtml(value).replace('\n', '<br/>') + '</li>');
        };
        $.connection.hub.disconnected(function () {
            setTimeout(function () {
                $.connection.hub.start();
            }, 5000);
        });
        $.connection.hub.start();
    });
</script>

但这在调用 /api/values 之前有效。所以我不认为是因为监控代码。此外,当 SignalR 失败时,整个服务器都会失败。从不同的 IP 地址进来也无法从 SignalR 获得信号。

如果监控页面没有起来,那么我看到了

Microsoft.Azure.AppService.Authentication Verbose: 0 : Received request: GET https://kamelos-app.azurewebsites.net/api/values
Microsoft.Azure.AppService.Authentication Verbose: 0 : Found 'AppServiceAuthSession' cookie for site 'xxx.azurewebsites.net'. Length: 768.
Microsoft.Azure.AppService.Authentication Verbose: 0 : Authenticated Tim Uy successfully using 'Session Cookie' authentication.
Exception thrown: 'System.Reflection.ReflectionTypeLoadException' in mscorlib.dll
'w3wp.exe' (CLR v4.0.30319: /LM/W3SVC/1848204182/ROOT-2-131345909532020945): Loaded 'D:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.Data.Services.Client\v4.0_5.6.2.0__31bf3856ad364e35\Microsoft.Data.Services.Client.dll'. Cannot find or open the PDB file.
05:32:22.128 [25] DEBUG  ValuesController - requested /api/values

更新 2。 问题是config.DependencyResolver = resolver。当这被注释掉时,SignalR 不会崩溃。我认为以某种方式运行 apiController 会导致解析器被转储。

是的,ApiController 完成的那一刻,我的 CustomNinjectDependencyResolver 就被释放了。为什么会这样?

【问题讨论】:

  • at first call, I do get a "Hello message" showing up in my SignalR logging clients (javascript webpages). However, immediately afterwards SignalR calls no longer work. 请分享您在 html 网页中使用的代码,以建立与 hub 的连接。如果您从 API 控制器操作调用 hub 方法向客户端推送通知,请分享调用 hub 方法的代码 sn-ps。
  • 试一试:从你的 ApiController 中移除 MobileAppController(它没有多大作用)。它仍然会杀死您的 SignalR 吗?如果是这样,这不是 azure-mobile-services 问题。 Azure 移动应用不再处理 S​​ignalR。
  • Fred,你有 bitbucket 用户名吗?我可以和你分享回购。这有点复杂,因为集线器是由记录器调用的。
  • Adrian,确实它被调用 ApiController(没有 MobileAppController)杀死了。我会相应地更改标题。
  • 问题是config.DependencyResolver = resolver;

标签: c# asp.net signalr azure-web-app-service


【解决方案1】:

原来有问题的行是

config.DependencyResolver = resolver

并且 WebApi 在 ApiController 被调用后处理解析器。见Why is ASP.NET Web Api's Dependency Resolver being disposed?

"Web API 每次请求都会调用 BeginScope() 方法一次 基础设施和 Web API 将调用 IDependencyScope.Dispose() 时 请求结束。”

【讨论】: