在我的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的基本内容......
答案 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存在之前单击提交按钮。我将把它作为读者的练习......