SignalR在IE和Firefox上与POST请求断开连接

时间:2017-02-14 16:51:37

标签: javascript c# internet-explorer firefox signalr

我在我的网站上设置了一个通知系统(ASP.NET MVC)。它需要无国籍,所以我选择了SignalR。 通知仅来自服务器端,我想在刷新新页面后显示,而不是实时显示。通常它是一个ActionResult,它将被触发并向客户发送通知,然后重定向或显示视图。

这是它的进展方式:

  1. 服务器向客户端发送消息。
  2. 客户端将其存储到浏览器本地存储。
  3. 重定向后,它会显示来自本地存储的通知。
  4. 我使用SignalR 2.2.1和JQuery 1.9.1

    NotificationHub.cs

    [HubName("notificationHub")]
    public class NotificiationHub : Hub
    {
        public static void Send(string connectionId, string name, string message)
        {
            var hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificiationHub>();
            hubContext.Clients.Client(connectionId).sendNotification(name, message);
        }
    }
    

    _Layout.cshtml js

    <script>
    getNotificationStack = function () {
        if (localStorage.getItem("notifications") != null) {
            return JSON.parse(localStorage.getItem("notifications"));
        }
        return [];
    }
    
    checkNotification = function () {
        var notificationStack = getNotificationStack();
    
        var count = notificationStack.length;
        for (var i = 0; i < count; i++) {
            window.alert(notificationStack.pop());
        }
        localStorage.removeItem("notifications");
    
    }
    
    initHubConnection = function () {
        // Reference the auto-generated proxy for the hub.
        $.connection.hub.logging = true;
    
        var notification = $.connection.notificationHub;
        notification.client.sendNotification = function (name, message) {
            var notificationStack = getNotificationStack();
            notificationStack.push(name + ": " + message);
            localStorage.setItem("notifications", JSON.stringify(notificationStack));
        };
    
        // Start the connection.
        $.connection.hub.start().done(function () {
            // allows to send notification to one tab through connectionId
            document.cookie = "connectionId=" + $.connection.hub.id;
        });
    }
    
    $(function () {
        checkNotification();
        initHubConnection();
    });
    </script>
    

    MyView.cshtml

    @using (Html.BeginForm("TestSendNotification", "Home", FormMethod.Post))
    {
        <input type="submit" value="Press that button"/>
    }
    

    HomeController.cs

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    
        public ActionResult TestSendNotification()
        {
            NotificiationHub.Send(Request.Cookies["connectionId"].Value, "Info", "Oh you pressed that!");
    
            return RedirectToAction("Index", "Home");
        }
    }
    

    它的功能类似于Chrome的魅力,但不适用于Microsoft Edge和Firefox。

    Microsoft Edge行为: 已建立连接,但客户端未收到消息。

    - &GT;注意:如果我调试我的应用并在发送通知后等待足够长时间,我会成功收到消息。 似乎发送消息需要太长时间,然后页面刷新,因此连接丢失,客户端无法获取消息...

    Firefox行为 每次我点击按钮(=发送POST),连接都会丢失,当然客户端无法获取消息。我不知道为什么会这样做!

    如果我使用ajax POST,它运行良好,但这不是我正在寻找的。

    ############################################### ###############

    2017年2月16日更新: 不幸的是我们不能在这个库中使用简单的http POST,它必须是ajax POST才能正常工作。 得到了他们的git答案:https://github.com/SignalR/SignalR/issues/3869#issuecomment-280074464

1 个答案:

答案 0 :(得分:0)

我们也必须处理这个问题。

解决方案是:跟踪新旧connectionIds。 在控制器内:

public ActionResult RegisterConnectionId(string newConnectionId, string oldConnectionId)
{
    Session["SignalRConnectionId"] = newConnectionId;

    if(!string.IsNullOrEmpty(oldConnectionId))
        RemapConnectionId(newConnectionId, oldConnectionId);        
}

我们引入了一个过期的MemoryCache作为旧ConnectionId的受害者缓存。

public void RemapConnectionId(string newConnectionId, string oldConnectionId)
{
    if(newConnectionId != null && newConnectionId != oldConnectionId)
        MemoryCache.Default.Set(oldConnectionId, newConnectionId, ...);
}

您可以从旧的ConnectionId获取当前的ConnectionId并处理潜在的MULTIPLE更改,如下所示:

public string ResolveConnectionId(string connectionId)
{            
    while (!IsEligibleConnectionId(connectionId) && !string.IsNullOrEmpty(connectionId))
        connectionId = MemoryCache.Default.Get(connectionId) as string;


    return connectionId;
}

当然,您需要在集线器启动时向服务器提供新的和旧的连接ID。