tpeczek/Lib.AspNetCore.ServerSentEvents

Single client clarity...

Closed this issue · 3 comments

After reading the documentation, I see that the User is exposed as well as the SendEventAsync. However, sending to a single client does seem to be a little more problematic for me.

Things I've tried:

  1. Getting a single connected client, selecting a client then sending an event:
var userId = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier));
_eventService.GetClient(userId).SendEventAsync("some message");
  1. Getting a client from a list of connected Clients:
var user = User;
_eventService.GetClients().Single(x => x.User == user).SendEventAsync("some message");

both of these result, for me, in a null reference error even though my unit tests show that get clients is populated with the existing claimsprincipal that defines User.

As a less secure work-around I'm just doing:

            var sse = new ServerSentEvent
            {
                Id = userId.ToString(),
                Type = messageType,
                Data = new List<string>{"some message"}
            };

return _seventService.SendEventAsync(sse); // TO ALL CLIENTS - on the server

and on the client,

        var source = new EventSource('/message-channel');
        source.onmessage = function(event) {
            console.log(event.data);
        }

        source.addEventListener('messageType', function (event) {
            alert(event.data);
        });

What is actually the CORRECT way of doing this?

Hi @leijae,

You first approach doesn't work, because IServerSentEventsService.GetClient expects ServerSentEventsClient.Id as a value.

If it comes to the second approach, I'm guessing you are doing this in context of a different request then the one which has established the connection. In that case, the User is in fact a different object just with the same values.

The correct approach is to use something unique from your claims to query for the correct client. I don't know the nature of your claims enough to give you 100% correct suggestion, but assuming your first approach comes from fact that you do have a unique value in name identifier, it could be something like this.

var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
_eventService.GetClients()
    .Single(x => x.User.FindFirstValue(ClaimTypes.NameIdentifier) == userId)
    .SendEventAsync("some message");

I also wouldn't be so brave to use Single unless you are really sure that user has an active SSE connection. I would rather go for null check and some logging.

Ok fantastic. Thanks for the clarity. Just wanted to show you what I'm doing here. With your help I did manage to get it working.

I have a bot using the MS Bot Framework, if a user wants to escalate to an agent they send a request goes to an available agent. So, using SSE, an agent can be anywhere in the application and get the request.

MS did a good job with MS Bot Framework, but they made using the bot as proxy concept incredibly difficult in practice...

image

Happy to hear that you've managed to get it working and even more happy that you are using this library for interesting stuff 👍.