[英]Signalr - It's possible to wait reponse from client?
我是使用 Signalr 的初学者,正在查看一些示例。
是否可以从服务器向客户端发送消息并等待其返回? 或者是否可以保证在回答后将使用相同的 session?
我的问题是因为在给定的过程中,在事务中,我需要询问用户是否要继续进行更改。 我之前无法问这个问题,因为验证应该在已进行更改(但尚未确认)的同一个 session 中完成。
重申Jaime Yule的评论,WebSockets是双向通信,不遵循请求/响应架构进行消息传递。 鉴于围绕WebSockets的通信非常流畅,这些要点对于您当前(和未来)的场景都要记住:
使用以下形式与Invoke等待并直接返回响应(就像“真正的”同步方法调用)
var persons = hubProxy.Invoke<IEnumerable<Person>>("GetPersonsSynchronous", SearchCriteria, noteFields).Result;
foreach (Person person in persons)
{
Console.WriteLine($"{person.LastName}, {person.FirstName}");
}
这是可能的,但我不建议(依赖)它。
而且这不是一个漂亮的解决方案(使用静态事件,对于这么简单的事情来说非常复杂)。
故事是这样的:
确保客户端和服务器知道connectionId
- 他们可能已经知道了,但我无法找到一种方法来访问它。
等待NotificationService.ConfirmAsync
...将在客户端打电话confirm
...将等待用户提供的答案
...并使用来自集线器的Callback
将其发送回服务器。
...将通过静态事件从NotificationService
通知回调
...将message
传回ConfirmAsync
(使用AutoResetEvent
)
...希望还在等待:)
客户端和服务器都有10秒的超时设置。
枢纽:
// Setup as /notification-hub
public class NotificationHub : Hub {
public string ConnectionId() => Context.ConnectionId;
public static event Action<string, string> Response;
public void Callback(string connectionId, string message) {
Response?.Invoke(connectionId, message);
}
}
服务:
// Wire it up using DI
public class NotificationService {
private readonly IHubContext<NotificationHub> _notificationHubContext;
public NotificationService(IHubContext<NotificationHub> notificationHubContext) {
_notificationHubContext = notificationHubContext;
}
public async Task<string> ConfirmAsync(string connectionId, string text, IEnumerable<string> choices) {
await _notificationHubContext.Clients.Client(connectionId)
.SendAsync("confirm", text, choices);
var are = new AutoResetEvent(false);
string response = null;
void Callback(string connId, string message) {
if (connectionId == connId) {
response = message;
are.Set();
}
}
NotificationHub.Response += Callback;
are.WaitOne(TimeSpan.FromSeconds(10));
NotificationHub.Response -= Callback;
return response;
}
}
客户端js:
var conn = new signalR.HubConnectionBuilder().withUrl("/notification-hub").build();
var connId;
// using Noty v3 (https://ned.im/noty/)
function confirm(text, choices) {
return new Promise(function (resolve, reject) {
var n = new Noty({
text: text,
timeout: 10000,
buttons: choices.map(function (b) {
return Noty.button(b, 'btn', function () {
resolve(b);
n.close();
});
})
});
n.show();
});
}
conn.on('confirm', function(text, choices) {
confirm(text, choices).then(function(choice) {
conn.invoke("Callback", connId, choice);
});
});
conn.start().then(function() {
conn.invoke("ConnectionId").then(function (connectionId) {
connId = connectionId;
// Picked up by a form and posted to the server
document.querySelector(".connection-id-input").value = connectionId;
});
});
对我来说,这是复杂的方式,将其纳入我正在进行的项目中。
它真的看起来会像以后会回来咬你的东西......
这现在可以通过 .NET 7 使用Client Results实现。
今天,我在 do.net 的 Github 页面上强调了这个问题,并得到了 SignalR 的一位开发人员的好评。
这需要服务器使用ISingleClientProxy.InvokeAsync才能向客户端发出请求并等待响应。
引用文档
除了调用客户端之外,服务器还可以向客户端请求结果。 这要求服务器使用 ISingleClientProxy.InvokeAsync 并且客户端从其 .On 处理程序返回结果。
从客户端 (js/ts)
hubConnection.on("GetMessage", async () => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(new { data: "message" });
}, 100);
});
return promise;
});
从服务器 (C#)
//By calling Client(...) on an instance of IHubContext<T>
async Task<object> SomeMethod(IHubContext<MyHub> context)
{
string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
"GetMessage");
return result;
}
//---------------------------//
//Or by calling Client(...) or Caller on the Clients property in a Hub method
public class ChatHub : Hub
{
public async Task<string> WaitForMessage(string connectionId)
{
var message = await Clients.Client(connectionId).InvokeAsync<string>(
"GetMessage");
return message;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.