【问题标题】:Using SignalR to notify current caller使用 SignalR 通知当前调用者
【发布时间】:2014-10-19 19:15:43
【问题描述】:

短版

在我的 POST 操作例程中,如何向客户端中运行的某些 JavaScript 发送通知?似乎 SignalR 应该是一个简单的解决方案,但我不知道如何在 POST 操作的客户端页面中对位于 JavaScript 中的 SignalR 客户端进行回调。

具体来说,我似乎需要一个“连接 ID”才能让 SignalR 与特定客户端对话,但我不知道如何在我的 POST 操作或客户端 JavaScript 中获取其中一个。

整个(不那么丑陋)的故事

在我的 MVC 应用中,我的 POST 操作可能需要很长时间才能完成。如果 POST 操作决定需要很长时间才能完成,我想通知页面上的一些 JavaScript,以便它可以向用户显示 “请稍候...” 通知。

这似乎是 SignalR 应该让我非常轻松的事情。在basic tutorial之后,我添加了一个Hub并在我的视图中创建了一个JS回调:

请注意,集线器没有方法。我的客户只需要一个只读通知。无需调用 Hub 上的任何方法向外界写入消息。

public class MyHub: Hub
{
}

该视图只有一个带有提交按钮的表单和一个隐藏的“请稍候”消息,SignalR 例程可以显示该消息。视图模型有一个字符串属性,我可以使用它来将 SignalR“连接 ID”传递给 POST 控制器(假设我知道如何获取它)。

@model SignalRTest.Models.MyViewModel

@using (Html.BeginForm()) {
    @Html.HiddenFor(m => m.SignalrConnectionId)
    <button type="submit" class="btn btn-primary">Go to it!</button>
}

<div id="hidden-msg" hidden="hidden">
    <p>Please wait...</p>
</div>

@section scripts {
    <!-- Reference the SignalR library. -->
    <script src="~/Scripts/jquery.signalR-2.1.2.min.js"></script>

    <!-- Reference the autogenerated SignalR hub script. -->
    <script src="~/signalr/hubs"></script>

    <!-- SignalR script to update the page -->
    <script>
        $(function () {
            // Get a reference to the server "hub" class (camelCase)
            var hub = $.connection.interviewDoneHub;

            // Get our connection ID and store it in a hidden field
            // so that it is sent to the POST action
            // var connectionId = $.connection.hub.id; //This doesn't work!
            // $('#@Html.IdFor(m => m.SignalrConnectionId)').attr(connectionId, '');

            // Create a function that the hub can call
            hub.client.myCallback = function () {
                $('#hidden-msg').show();
            };

            // Start the connection.
            $.connection.hub.start().done(function () {
            });
        });
    </script>
}

同时在我的 POST 控制器操作中,我调用了 JS 回调:

[HttpPost]
public async Task<ActionResult> MyAction(MyViewModel model)
{
    // We've decided that this will take a while.  Tell the client about it...
    var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
    context.Clients.All.myCallback();
    //context.Clients.Client(model.SignalrConnectionId).myCallback();

    await Task.Delay(2000);
    return RedirectToAction("NextPage");
}

现在,我的问题是:作为概念验证测试,我使用这段代码来调用 JS 回调:

var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.All.myCallback();

这只是花花公子。但是,显然,我想调用与 POST 操作关联的特定客户端。 C# Intellisense 告诉我应该可以调用

context.Clients.Client("connectionId").myCallback();

但我不知道如何获得所需的“connectionId”字符串。我认为我无法在 POST 控制器中获取客户端 ID,因为那时我没有任何类型的 SignalR 连接到客户端。我想我会让客户端获取它的连接 ID 并将其提供给视图模型中的 POST 控制器,但我还没有找到从 SignalR 框架中获取连接 ID 的神奇 JS 代码。

我发现了几篇 SO 文章中的事实陈述:

connectionId = $.connection.hub.id;

但这会返回undefined。我找到了这个SignalR JS Client Wiki page,上面写着:

连接.id
获取或设置当前连接的客户端ID

但这也会返回undefined

这又回到了我原来的问题。也许我不了解连接 ID 是什么的基本知识......

【问题讨论】:

  • 您的 MVC 控制器是否有原因不能在需要时简单地连接到您的集线器,然后简单地告诉集线器发送此消息并为其提供您想要通知的 connectionId?
  • 这就是问题的重点。我不知道如何获取连接 ID。

标签: javascript asp.net asp.net-mvc asp.net-mvc-4 signalr


【解决方案1】:

好的,我相信我明白了问题所在。你想在 javascript 中开始你的连接

$.connection.hub.start()

完成此操作后,您应该能够执行 connection.hub.id - 如果未启动连接,则根本没有连接 ID,这就是您获得“未定义”值的原因,因为它在您开始连接之前尚未设置。


实际上,这可能是一个混蛋,不确定它是否会起作用,但应该对您的代码进行最少的更改。每次客户端连接到您的集线器时,您都可以将其添加到连接方法中

Groups.Add(Context.ConnectionId, Context.ConnectionId); 
// name it the same as your connection id since that is the identified you are using

然后在您的控制器中,您可以在操作调用中添加以下内容

var context = GlobalHost.ConnectionManager.GetHubContext<InterviewDoneHub>();
context.Clients.Groups(connectionId).myCallback();

如果上述方法不起作用,那么您将不得不使用.NET Client 连接到集线器。就像您从 javascript 连接一样,您可以使用 C# 连接到集线器并调用一个方法,然后该方法将通知您想要通知的客户端。我过去使用 WebAPI 完成了这项工作,但我相当肯定它也可以在 Asp.Net MVC 中完成。

【讨论】:

  • 是的,我试图在客户端 ID 存在之前访问它。如果我将 $.connection.hub.start() 调用移动到连接完成例程,那么它会按预期工作。但是现在我遇到了另一个问题:如果我在建立连接之前单击提交按钮,那么我仍然会收到一个空白 ID 字符串发送到 POST 例程。我需要在页面加载时禁用提交按钮,并在建立连接后重新启用它。叹息……
  • @Bob.at.SBS - 但是建立连接不应花费太长时间。一旦启动完成,您应该能够启用此功能,并且可能还在那里定义您的 onclick 事件。
【解决方案2】:

感谢 GJohn 对 .NET Client docs 的引用,其中包含以下示例:

$.connection.hub.start()
    .done(function(){ console.log('Now connected, connection ID=' + $.connection.hub.id); })
    .fail(function(){ console.log('Could not Connect!'); });
});

所以,事实上 $.connection.hub.id 确实返回了连接 ID。但是在建立连接之前,我试图在页面加载时调用该例程!我的视图的正确 JS 代码如下所示:

@section scripts {
    <!-- Reference the SignalR library. -->
    <script src="~/Scripts/jquery.signalR-2.1.2.min.js"></script>

    <!-- Reference the autogenerated SignalR hub script. -->
    <script src="~/signalr/hubs"></script>

    <!-- SignalR script to update the page -->
    <script>
        $(function () {
            // Get a reference to the server "hub" class (camelCase)
            var hub = $.connection.interviewDoneHub;

            // Create a function that the hub can call
            hub.client.myCallback = function () {
                $('#hidden-msg').show();
            };

            // Start the connection.
            $.connection.hub.start()
                .done(function () {
                    // Get our connection ID and store it in a hidden field
                    // so that it is sent to the POST action
                    $('#@Html.IdFor(m => m.SignalrConnectionId)').attr('value', $.connection.hub.id);
                })

                .fail(function () { });
        });
    </script>
}

但是现在我有一个小问题:我需要添加更多的 JS 代码来禁用表单加载时的提交按钮,并在连接“完成”功能中重新启用它;否则用户可以在连接 ID 存在之前点击提交按钮。我将把它作为练习留给读者......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多