How can I manage to have only one connection of signalR per user in different browsers/tabs? so if user is active in one tab/browser and wants to open another tab/browser I have to alert him to first close the previous tab/browser. like Whatsapp.
I have implemented this so far but I don't know how to fill the commented par?!
public override async Task OnConnectedAsync()
{
bool isUserExisted = _onlineUsers.TryAdd(Context.User.FindFirst("username").Value, Context.ConnectionId);
if (!isUserExisted)
{
//avoid to connect the already connected user
//and send alert to him
}
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
_onlineUsers.TryRemove(Context.User.FindFirst("username").Value, out _);
await base.OnDisconnectedAsync(exception);
}
Any help would be appreciated.
I'm not an expert on the client side, but I assume you cannot maintain the same SignalR connection from different tabs because each tab has its own context and establishes its own connection.
You can, however, on the server side create a user group, where you would pack all connections of the current user:
var user = GetUser(username); // Get your user from context
await this.Groups.AddToGroupAsync(this.Context.ConnectionId, GetUserGroupName(user)); // Generate unique group name per user and add all user connections to it
Than you can play with whom you send your events, e.g. send to all user connections but for current one (i.e. but for current tab):
this.ctx.Clients.GroupExcept(groupName, currentConnectionId);
I'm not sure when alerting should occur? Do you call a REST method on the server? Or directly a SignalR method? If it's an independent REST method, you could pass your current SignalR connection id in a custom header and read it in the hub.
Related
I'm trying to send a message using SignalR and it works if I send to everybody, but not to s specific user. I tried to use the ConnectionId that in theory should be unique, but every time I tried to use the same connectionId that I received by the client, it doesn't work.
The server-side:
public async Task SendMessage(string user, string message)
{
var a = Context.UserIdentifier;
await Clients.User(Context.ConnectionId).SendAsync("ReceiveMessage", user, message);
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Trying to pass the same ConnectionId in context doesn't send the message, only when I call Clients.All
The client is an android app and I'm not sure if I should register something on my client-side.
hubConnection = HubConnectionBuilder.create("http://192.168.1.5:3000/notification").build()
hubConnection.start()
hubConnection.on<String, String>(
"ReceiveMessage",
Action2 { user: String?, message: String? ->
requireActivity().runOnUiThread(java.lang.Runnable {
Toast.makeText(
context,
"I'm here.",
Toast.LENGTH_LONG
).show()
})
},
String::class.java,
String::class.java
)
You should use
await Clients.Client(Context.ConnectionId).SendAsync("ReceiveMessage", message);
It should works well.
SignalR allows messages to be sent to a particular client connection, all connections associated with a specific user, as well as to named groups of connections. => await Clients. User(userId).
if you want to send message to specific user in SignalR, easiest way is the use Form authentication. Also you can use your custom session with form authentication. Right after creation your session code put this code. FormsAuthentication.SetAuthCookie (username.Trim (), false); Then in signalR you can use this line for send message to this user:
I'm creating a SignalR server and I added a couple of rules that the clients should follow when they want to connect to server.
The rules (also call them 'validators') are, for example, that a certain header should be present when the client request to connect.
My question is: how can "reject" a connection with a proper "status code" and "message" and kick out the user?
I didn't find any helpful thread around.
Thanks for reading.
I Checked the hub class and found:
and if the connection is assciated with httprequest,you could use Context.GetHttpContext() method to get the httpcontext,
So I tried as below:
public override async Task OnConnectedAsync()
{
var errormessage = "the connection was disconnected due to Some reason";
var header = Context.GetHttpContext().Request.Headers;
if (header.ContainsKey("Origin"))
{
await Clients.Caller.SendAsync("Disconnect", errormessage);
Context.Abort();
.......
}
}
The Result:
I have a Blazor server side application that is using Azure AD authentication. I would like to get some data about the current user from a database or web API and cache it when they first log in and then be able to use it during the session without fetching from the source every time it's needed.
I am unclear where to put this or if there is even an event I can add a handler for.
Are there any Blazor life cycle or other events where this can be done reliably?
Update
I found that the AddMicrosoftIdentityWebApp method has an optional overload where events can be connected.
// Azure AD authentication settings....
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
Configuration.Bind("AzureAd", options);
options.Events ??= new OpenIdConnectEvents();
options.Events.OnTokenValidated += OnTokenValidatedFuncAsync;
});
Then the OnTokenValidatedFuncAsync handler in StartUp.cs is:
private async Task OnTokenValidatedFuncAsync(TokenValidatedContext context)
{
var userName = context.Principal.Identity.Name;
await Task.CompletedTask.ConfigureAwait(false);
}
This returns name of the user that just connected and appears to be the event that I am looking for.
Now the problem is that I need to access the DI container to update an instance of a user object that will store the settings I plan to retrieve. I've tried a few options but I'm having trouble accessing the DI container from here. I attempted to use the [FromServices] attribute and inject the object but that does not work in this event handler. How can I access the DI container in this code?
I have an asp.net mvc 4 app that uses SignalR.
When an user is connected I want to notify everyone of that (sending to "all" for the moment just to test it). In my Hub clas I have this:
public override System.Threading.Tasks.Task OnConnected()
{
NotifyAllOfUserLogin();
return base.OnConnected();
}
In the _layout.cshtml, I have this:
<script type="text/javascript">
$(document).ready(function () {
var proxy = $.connection.messagehub;
proxy.client.messageAll = function (message) {
$('#messages').prepend('<p style=\'white-space:pre;\'>' + message + '</p><br />');
};
$("#btnSubmitMessage").click(function () {
proxy.server.messageAll($("#txtMessage").val());
$("#txtMessage").val('');
});
$.connection.hub.start();
});
</script>
While this works, I think writing this in the masterpage is a mistake, since the hub connection will be reinitialized for every page that inherits the master, so OnConnected will be called a lot of times.
How should I deal with this properly, calling OnConnect only when the user logs into the application, and onDisconnected when the user logs out?
For every page-load, a new connection is made. When that page is closed, similarly that specific connection is closed.
What you will want to do is keep track of the user's connection ID's on the server-side. You will need to keep track of the active connections related to each specific user account, and also when they disconnect. Doing so, you can notify all users that a person connected if there are no pre-existing active connection ID's related to that user.
I'm using SignalR 1 with MVC4 C# web application with form authentication.
I have a code in my layout page in JavaScript :
$(documnet).ready(function(){
connect to hub code ...
})
I want to disconnect a user form the hub and start connect again after he does a login and validate ok.
I want to do it from server side inside my account controller and method :
public ActionResult LogOn(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (System.Web.Security.Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
....here , disconnect from hub
....to make the user reconnect
}
The reason I want to do it is because SignalR throws an error if user changed to authenticated after login and the connection remains . The error is:
The connection id is in the incorrect format.
You cannot stop and start SignalR connections from the server. You will need to call
$.connection.hub.stop(); //on the client before the user attempts to log on and then call
$.connection.hub.start(); //after the log on attempt has completed.
One way you could do what you ask is to write a disconnect event on your client that the server can call through SignalR. Maybe something somewhat like this:
myHub.client.serverOrderedDisconnect = function (value) {
$.connection.hub.stop();
};
Then, on the server, something like this:
Clients.Client(Context.ConnectionId).serverOrderedDisconnect();
If someone is still looking for solution(SignalR version 2.4.1):
GlobalHost.DependencyResolver.Resolve<ITransportHeartbeat>().GetConnections().First(c => c.ConnectionId == "YourId").Disconnect();
Try controlling everything from javascript. The following is a logout example, login would be similar.
From http://www.asp.net/signalr/overview/security/introduction-to-security:
If a user's authentication status changes while an active connection
exists, the user will receive an error that states, "The user identity
cannot change during an active SignalR connection." In that case, your
application should re-connect to the server to make sure the
connection id and username are coordinated. For example, if your
application allows the user to log out while an active connection
exists, the username for the connection will no longer match the name
that is passed in for the next request. You will want to stop the
connection before the user logs out, and then restart it.
However, it is important to note that most applications will not need
to manually stop and start the connection. If your application
redirects users to a separate page after logging out, such as the
default behavior in a Web Forms application or MVC application, or
refreshes the current page after logging out, the active connection is
automatically disconnected and does not require any additional action.
The following example shows how to stop and start a connection when
the user status has changed.
<script type="text/javascript">
$(function () {
var chat = $.connection.sampleHub;
$.connection.hub.start().done(function () {
$('#logoutbutton').click(function () {
chat.connection.stop();
$.ajax({
url: "Services/SampleWebService.svc/LogOut",
type: "POST"
}).done(function () {
chat.connection.start();
});
});
});
});
You can the hub context to abort() the connection directly in .Net Core
Context.Abort();
See the method below
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.signalr.hubcallercontext.abort
Try this:
public ActionResult LogOn(LoginModel model, string returnUrl) {
if (ModelState.IsValid) {
if (System.Web.Security.Membership.ValidateUser(model.UserName, model.Password)) {
FormsAuthentication.SetAuthCookie(model.UserName, false);
connection.Stop();
}
}
Assuming your connection handle is connection. The challenge is accessing a handle to your connection object in your Action Method.
Copy and paste the following function into your Hub
Use HttpContext.Current.Response.End();
to force the client to disconnect in your hub