繁体   English   中英

Signalr - 可以等待客户端的响应吗?

[英]Signalr - It's possible to wait reponse from client?

我是使用 Signalr 的初学者,正在查看一些示例。

是否可以从服务器向客户端发送消息并等待其返回? 或者是否可以保证在回答后将使用相同的 session?

我的问题是因为在给定的过程中,在事务中,我需要询问用户是否要继续进行更改。 我之前无法问这个问题,因为验证应该在已进行更改(但尚未确认)的同一个 session 中完成。

重申Jaime Yule的评论,WebSockets是双向通信,不遵循请求/响应架构进行消息传递。 鉴于围绕WebSockets的通信非常流畅,这些要点对于您当前(和未来)的场景都要记住:

  • SignalR非常棒,如果您要将它用于火灾并忘记(向用户显示弹出窗口,那就是它)
  • 它不是像你要求的那样围绕请求 - 响应而设计的,并且尝试使用它本身就是一种反模式
  • 消息可以随时从连接的任一端发送,并且对一条消息没有本机支持以指示它与另一条消息相关
  • 这使得协议不适合交易要求

使用以下形式与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;
        });
    });

对我来说,这是复杂的方式,将其纳入我正在进行的项目中。
它真的看起来会像以后会回来咬你的东西......

是否可以从服务器向客户端发送消息并等待它的返回? 或者是否可以保证在回答之后将使用相同的会话?

这一切都不可能。 目前没有办法等待客户端的响应,甚至无法知道客户端是否收到了消息。 GitHub 上有一些关于实现这个的讨论。 还有这里的功能请求

在此之前,解决方法是从服务器发送一个“通知”,并以“即发即忘”的态度让客户端通过 HTTP 请求向服务器发送所需的数据。

这现在可以通过 .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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM