使用SignalR通知当前呼叫者

时间:2014-10-19 19:15:43

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

短版

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

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

整个(不那么难看)的故事

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

这似乎是SignalR对我来说非常容易的事情。在basic tutorial之后,我添加了一个Hub并在我的视图中创建了一个JS回调:

请注意,Hub没有方法。我的客户端只需要一个只读通知。它无需调用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,其中说:

  

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

但也会返回undefined

回到原来的问题。也许我不了解连接ID的基本内容......

2 个答案:

答案 0 :(得分:1)

好的,我相信我知道这是什么问题。您想在javascript中启动您的连接

$.connection.hub.start()

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


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

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中完成。

答案 1 :(得分:0)

感谢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存在之前单击提交按钮。我将把它作为读者的练习......