I am a beginner in using Signalr and am checking out some examples.
Is it possible to send a message to the client from the server and wait for a return from it? Or is it possible to guarantee that after the answer the same session will be used?
My question is because in a given process, within a transaction, I need to ask the user if he wants to continue with the changes. I have not been able to ask this question before because validations should be done in the same session where changes have been made (but not yet confirmed).
Reiterating the comment from Jaime Yule, WebSockets are bidirectional communication and do not follow the Request/Response architecture for messaging. Given the very fluid nature of communication around WebSockets, these bullet points are good to keep in mind for your current (& future) scenarios:
It is possible, but i would not recommend (relying on) it.
And it's not a pretty solution (using a static event and being pretty complex for such a simple thing).
Story goes like this:
Make sure client and server know the connectionId
- They probably know that already, but i could not figure out a way to access it.
Await NotificationService.ConfirmAsync
... which will call confirm
on the client
... which will await the user supplied answer
... and send it back to the server using Callback
from The hub.
... which will notify the Callback from the NotificationService
over a static event
... which will hand off the message
back to ConfirmAsync
(using a AutoResetEvent
)
... which is hopefully still waiting :)
Client and server both have a 10 second timeout set.
The hub:
// 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);
}
}
Service:
// 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;
}
}
Client side 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;
});
});
For me this is way to complex to put it into the project i am working on.
It really looks like something that will come back and bite you later...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With