Can't call web api controller inside the signalr OnDisconnected method - asp.net-mvc

I have an mvc web apllication with signalr and i want to update the table in the published web api.
calling web api controller to get users inside the Onconnected method works fine:
public override async Task OnConnected()
{
var users = await _client.GetAsync("chats/users");
Clients.All.userConnected();
}
But when i place the code inside the OnDisconnected method it gives me an error:
public override async Task OnDisconnected(bool stopCalled)
{
var users = await _client.GetAsync("chats/users");
}
Why is this happening? this is the whole Hub code:
private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
private HttpClient _client;
public ChatHub()
{
AccessDelegatingHandler handler = new AccessDelegatingHandler();
_client = HttpClientFactory.Create(handler);
_client.BaseAddress = new Uri(ClsConfig.GetConfiguration().APIBaseAddress);
}
// Send new message to group chat.
public static void SendGroupMessage(MessageDetailModel messageDetail)
{
hubContext.Clients.All.newGroupMessageReceived(messageDetail);
}
public override async Task OnConnected()
{
var users = await _client.GetAsync("chats/users");
Clients.All.userConnected();
}
public override Task OnReconnected()
{
return base.OnReconnected();
}
public override async Task OnDisconnected(bool stopCalled)
{
var users = await _client.GetAsync("chats/users");
}
EDIT:
I found out that when i place var user = Context.User.Identity; inside the OnDisconnected method the user is IsAuthenticated = true but when i place a break point inside the AccessDelegatingHandler class the var identity = (ClaimsIdentity)HttpContext.Current.User.Identity; line gives an error and is IsAuthenticated = false

By the time the onDisconnected event fires, you are likely already disconnected, and there is no guarantee that your code will run, (its a known issue with Signalr) also are you monitoring the onDisconnected in the client or the server? It looks like you are trying to handle it from the server, and you should be handling it from the client.
This link should help to understand why this is the way it is.
https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/handling-connection-lifetime-events#clientdisconnect

Related

Identity Server 4 Multi-tenancy logout

I'm currently developing an identity server. It is multi-tenant with multiple user repositories.
I am able to pass (using Services.OpenIDConnect.Options) my tenant details from my MVC to the IDS in order to select the appropriate user repository on login
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("Tenant", "TenantDetail");
return Task.CompletedTask;
};
I am attempting to obtain this same information for logout, however the initial call to logout has some back end process that calls CustomProfileService.IsActiveAsync(IsActiveContext context).
I am unable to obtain the tenant information from the IsActiveContext, nor am i able to read any kind of query string (as i was using for login).
Any suggestions, or even alternative methods that might be more correct than what I'm attempting, would be greatly appreciated.
OnRedirectToIdentityProvider will not be hit on signout. You'll need to pass the tenant information in the OnRedirectToIdentityProviderForSignOut event in your client instead.
Here's a snippet, that's far from complete:
services
.AddOpenIdConnect("oidc", options =>
{
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProviderForSignOut = context =>
{
context.ProtocolMessage.AcrValues = "tenant:TenantDetail";
return Task.CompletedTask;
},
}
}
In IdentityServer you'll need to lookup the acr_values in the query parameters from the request. Inject IHttpContextAccessor in order to access the context:
public class ProfileService : IProfileService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ProfileService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// ...
}
public async Task IsActiveAsync(IsActiveContext context)
{
// Please note that this method is called on many occasions. Check context.Caller
// This means that you'll have to make sure that the acr_valus are present on all
// ocassions, hence the question in my comment.
var request = _httpContextAccessor.HttpContext.Request;
if (request.Method == HttpMethods.Get)
{
// acr_values should be present on all ocassions.
var values = (string)request.Query["acr_values"];
// This is just a sample, you'll need to parse the values.
var tenant = values.Split(':')[1];
}
// Your code where you link the repository ...
var sub = context.Subject.GetSubjectId();
var user = await userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
Please let me know if this solves the issue for you.

Why Signalr does not stable all time?

I am workign witha a signalr project using .Net framework 4.6. And I have a base controller:
public abstract class Base<THub> : ApiController where THub : IHub
{
private static readonly Func<IHubContext> ValueFactory = () => GlobalHost.ConnectionManager.GetHubContext<THub>();
private readonly Lazy<IHubContext> hub = new Lazy<IHubContext>(ValueFactory);
protected IHubContext Hub => hub.Value;
}
SO I am creating my Notification controller from Base.
public class NewsController : Base<NotificationHub>{
public async Task<IHttpActionResult> CreateNews(string name){
// connect database
// create news on database
....
Hub.Clients.All.send(name);
}
}
And I am connecting this hub from my desktop applicaiton. I am creating news using CreateNews(string name)action, this action sends notification at first few attempts.
And then it does not send a few attempts, and some times later again sends notification to client.
[HubName("notification")]
public class NotificationHub: Hub
{
private static readonly ConnectionMapping<string> Connections = new ConnectionMapping<string>();
public override Task OnConnected()
{
var name = Context.User.Identity.Name;
Connections.Add(name, Context.ConnectionId);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
var name = Context.User.Identity.Name;
Connections.Remove(name, Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
var name = Context.User.Identity.Name;
if (!Connections.GetConnections(name).Contains(Context.ConnectionId))
{
Connections.Add(name, Context.ConnectionId);
}
return base.OnReconnected();
}
}
I set break points in my desktop client, there is no error or connection failures. It always works. But notifications does not send all time called CreateNews(string name)action.
What are the possible causes?
Whenever your desktop application start, you need to add that connectionid in your hub class object. so you need to implement your signalR object in desktop app as well.
so now when you create news from the desktop app, it will call hub class directly where you will get all active connections, where you want to send notification.
so you need to implement signalR Both side.

Is it possible to use Audit.Net with httpClient to capture external requests

Using Audit.Net is it possible to create an audit scope for httpClient requests, in a similar way to the MVC.Core or WebAPI.Core Middleware?
I've tried something like this but not having much success, generally it'll timeout the app.
AuditScope scope = null;
try {
using(HttpClient client = new HttpClient) {
scope = await AuditScope.CreateAsync("",() => client)
// code to initialise the Httpclient
}
}
finally {
await scope.DisposeAsync();
}
I think the only option to hook into the HttpClient is to use a custom HttpClientHandler so you can intercept the rest calls.
Just as an example:
public class AuditClientHandler : HttpClientHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var options = new AuditScopeOptions()
{
EventType = $"{request.Method.Method} {request.RequestUri.AbsoluteUri}",
CreationPolicy = EventCreationPolicy.InsertOnStartReplaceOnEnd,
ExtraFields = new
{
request = GetRequestAudit(request)
}
};
using (var scope = AuditScope.Create(options))
{
var response = await base.SendAsync(request, cancellationToken);
scope.SetCustomField("response", GetResponseAudit(response));
return response;
}
}
}
I've used the InsertOnStartReplaceOnEnd creation policy, so the request is saved before it's sent to the server, and the response is added to the event and saved afterwards.
The implementation of GetRequestAudit / GetResponseAudit is up to you, just return an object (that can be serialized) with the information you want to log.
So each time you need to audit an HttpClient instance, you need to pass the handler to its constructor:
var cli = new HttpClient(new AuditClientHandler());
var response = await cli.GetAsync("http://google.com");
Anyway I will evaluate providing a new library (Audit.HttpClient?) with a configurable Handler so the implementation could be cleaner.
Update
You can now use the Audit.HttpClient extension for a cleaner implementation. Take a look at the documentation here

Orleans two way communication between grain client and grain

I would like to implement a callback to a grain client inside a grain method Eg. during evaluation of Grain.Method1 a client method needs to be called to get some data.
I've tried to do so with streams, but when I subscribe the stream on client, the method does not fire.
Grain:
var config = ClusterConfiguration.LocalhostPrimarySilo();
config.AddMemoryStorageProvider();
config.Globals.RegisterStorageProvider<MemoryStorage>("PubSubStore");
config.Globals.RegisterStreamProvider<SimpleMessageStreamProvider>("MySMSProvider");
...
public override async Task OnActivateAsync() {
var streamProvider = GetStreamProvider("MySMSProvider");
var stream = streamProvider.GetStream<MyTypeMessage>(myGuid, "MyStream");
RegisterTimer(s => {
return stream.OnNextAsync(new MyTypeMessage());
}, null, TimeSpan.FromMilliseconds(1000), TimeSpan.FromMilliseconds(1000));
...
Client:
var clientConfiguration = ClientConfiguration.LocalhostSilo();
clientConfiguration.RegisterStreamProvider<SimpleMessageStreamProvider>("MySMSProvider");
GrainClient.Initialize(clientConfiguration);
...
var streamProvider = GrainClient.GetStreamProvider("MySMSProvider");
var stream = requestStreamProvider.GetStream<MyTypeMessage>(myGuid, "MyStream");
await stream.SubscribeAsync(
async (message, token) => { process message that does not fire });
Looks like you are not keeping the reference to StreamSubscriptionHandle<MyTypeMessage> which is returned by subscription
Try something like this :
var subscriptionHandle = await _factCalculationRequestStream
.SubscribeAsync(async (message, token) => { process message that does not fire });
and keep the subscriptionHandle from being garbage collected
In my case I ended up implementing IGrainObserver on my class (which is a websocketBehaviour in a WebAPI project and this subscription works well:
public class MySocketService: WebSocketBehavior, IGrainObserver, IDisposable
{
private StreamSubscriptionHandle<MyStreamEvent>
...
//inside some method invoked externally
_streamSubscriberHandle = await requestedStream
.SubscribeAsync(onStreamMessage, onStreamError, onStreamComplete);
}

Invoke javascript method from ASP.NET MVC controller using SignalR

I am using SignalR in my application in order to be able to automatically invoke one JavaScript method whenever a specific action method of a ASP.NET MVC controller is accessed/hit/invoke. Everything works fine on the first hit. But when I try to hit the controller action second time my HttpWebRequest times out. Following is my controller:
public class ShowRoomHubController : Controller
{
public void UpdateDressingRoomData()
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ShowRoomHub>();
hubContext.Clients.All.acceptGreet();
}
}
My hub is as follow:
public class ShowRoomHub : Hub
{
public void Hello()
{
Clients.All.hello();
}
public void GreetAll()
{
// Call the addNewMessageToPage method to update clients.
Clients.All.acceptGreet("test now");
}
}
Calling UpdateDressingRoomData action from HttpWebRequest or HttpClient works for first time only then it starts timing out. Here is my console application code (HTTP client):
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:81/ShowRoomHub/UpdateDressingRoomData");
var response = request.GetResponse();
response.Close();
Or
var httpClient = new HttpClient();
var response = httpClient.GetAsync("http://localhost:81/ShowRoomHub/UpdateDressingRoomData").Result;
Here is my javascript client side code:
connection = $.hubConnection();
//Creating proxy
this.proxy = connection.createHubProxy('showRoomHub');
connection.logging = true;
//Publishing an event when server pushes a greeting message
this.proxy.on('acceptGreet', function () {
//alert('message');
$rootScope.$emit("acceptGreet", null);
});
//Starting connection
connection.start().done(function () {
//alert('started');
});
Any idea what's wrong in my code?

Resources