Base solution for your next web application
Open Closed

Xamarin push notifications using signalr abpcommonhubpath /signalr #11335


0
mgc created

Prerequisites

  • What is your product version? v11.3.0
  • What is your product type (Angular or MVC)? MVC
  • What is product framework type (.net framework or .net core)? .net core

Hi,

I use notificationPublisher to send a notification to all users using the below code and it works for the web application. await _notificationPublisher.PublishAsync(AppNotificationNames.NewIncident, notificationData,severity: NotificationSeverity.Warn);

But when I connect, using signalr, to abpcommonhub in xamarin application (connection established successfully based on https://support.aspnetzero.com/QA/Questions/6841/Push-notification-implementation-in-Xamarin-Mobile-application) and subscribe to above notification message, I cannot receive it with the below hubconnection message subscription code. HubConnection.On(AppNotificationNames.NewIncident, (message) => { Console.WriteLine("TEST"); });

Below is the code of NotificationService in Xamarin application.

public class NotificationService : ISingletonDependency, INotificationService { public HubConnection HubConnection { get; }

private readonly IAccessTokenManager _accessTokenManager;
private bool _isConnecting;

public NotificationService(IAccessTokenManager accessTokenManager)
{
    try
    {
        _accessTokenManager = accessTokenManager;

        HubConnection = new HubConnectionBuilder()
            .WithUrl(CreateHubUrl(),

            (opts) =>
                {
                    opts.HttpMessageHandlerFactory = (message) =>
                    {
                        if (message is HttpClientHandler clientHandler)
                            // bypass SSL certificate
                            clientHandler.ServerCertificateCustomValidationCallback +=
                                (sender, certificate, chain, sslPolicyErrors) => { return true; };
                        return message;
                    };
                }
            )
            .ConfigureLogging(logging => {
                logging.SetMinimumLevel(LogLevel.Debug);
                logging.AddConsole();
                logging.AddDebug();
            })
            .Build();


        HubConnection.On<string>(AppNotificationNames.NewIncident, (message) =>
        {
            Console.WriteLine("TEST");
        });


        HubConnection.Closed += TryToConnectIfNeeds;

        ConnectAsync().ConfigureAwait(true);
    }
    catch (Exception ex)
    {
        ExceptionHandler.LogException(ex);
    }
}

private async Task TryToConnectIfNeeds(Exception connectionException)
{
    try
    {
        ExceptionHandler.LogException(connectionException);

        if (!_accessTokenManager.IsUserLoggedIn)
        {
            return;
        }

        await Task.Delay(new Random().Next(0, 5) * 1000);
        await HubConnection.StartAsync();
    }
    catch (Exception ex)
    {
        ExceptionHandler.LogException(ex);
    }
}

public async Task<bool> ConnectAsync()
{
    try
    {
        if (HubConnection.State == HubConnectionState.Connected)
        {
            return true;
        }

        if (_isConnecting)
        {
            return false;
        }

        _isConnecting = true;

        await HubConnection.StartAsync();

        Console.WriteLine(@"Connected!");
        return true;
    }
    catch (Exception ex)
    {
        ExceptionHandler.LogException(ex);
        return false;
    }
    finally
    {
        _isConnecting = false;
    }
}


public async Task<bool> DisconnectAsync()
{
    try
    {
        if (HubConnection.State == HubConnectionState.Disconnected)
        {
            return true;
        }

        await HubConnection.StopAsync();

        Console.WriteLine(@"Disconnected!");
        return true;
    }
    catch (Exception ex)
    {
        ExceptionHandler.LogException(ex);
        return false;
    }
}

private string CreateHubUrl()
{
    string accessToken = _accessTokenManager.AuthenticateResult.EncryptedAccessToken;
    return ApiUrlConfig.BaseUrl +
           AppConsts.SignalRAbpCommonHubPath.TrimStart('/')
           + "?" +
           AppConsts.EncryptedAuthTokenQueryStringName + "=" +
           Uri.EscapeDataString(accessToken);
}

}`

Could you please help me?
Thank you in advance!

14 Answer(s)
  • 0
    admin created

    Hi,

    • Did you start the host app using the bat file included under YourPRojectName.Host/ folder ?
    • Could you also share the result of CreateHubUrl method ?

    Thanks,

  • 0
    mgc created

    Hi,

    I start Web.Host from Visual Studio with Mobile profile, but I tried the bat file also. The CreateHubUrl result is https://10.0.2.2:44301/signalr?enc_auth_token=Jqd586PETXlUKqd7js8a6xjlbaFHQkj%2F9v3QPtCkH%2BS9y7AGW%2FP27nGie6aesVvtENCI6ic4msJ5MYbjR5mSchJZiTX1xJw%2BnVT8jMo7nhhvmgc7DhggTkjY36wZT97eWJaZiBgpL4hrTZAjC2xiMetYpgZsOieRACbXIgwpcnJ59X4XtXfgCOAXwpo5ZrxS4N3GtCAqu2aEwHFRX3XFgS870AxMtG5YQjAV2e%2FAnNz0JlXyLROBR1WKuKb5E5Q4jNABhHOCm4r55SO6wzIe1sFikWSx6nM0qF7AkX%2FMg21HyE0aepBfMjUnp2N2XeZBmNdnz9zK07%2FnBZ%2F5qhY%2Byo4GmZW0joYOg4YBvUAKQgrPcIoifB3VhaWGBg0fypfFcrJIRrF0qm5I5J%2BN3e5WpMmjfKXw0GP9jfAfYzBG5zotKNH%2Bzo6QKh1naSVpYYFo8hdBgsXukskwT46xJhFdL8cUqdmdZa9kAkJc4lkSA5hPf2BF1JdAAeCwOaCE6DEZCY%2FamSY33Hx1hdJVHpGbEP9c4GsXxoeWV%2BxO5fOCmajkJ5j9V3P5RSsdjjeuiQCDQcGLwhUFgmF%2Bk8nYA2ysPyWwme1e1%2Bc7WC1okDc3hAwUqBAhL4sZla4TMb4OIUiR1uPnNwUnM7DYrdy3I0mGbA4Bug9eoS2dPPCfTsuIZbbfr52YLOMG0a79kIfuvz1C810rx8%2B%2FeZbtluZd%2FvZ3sDUQVI8fj6NZ91ADLUUCC7FSbAsJvOmkmsYqm1nsyrM6jZbCqC5Dcsa%2Bb4OrimVz1ZGLHSMb6bFSdnZiQ1Lj54qk0xkuwBr%2FHt7HiHwZCM6HccfOCgRG%2BLzNiC9GEYd%2Bjt1%2FKblxBIbXv1wlC5mwLhXVaDC2%2Bu0gq%2BbZzHShVv3J%2B%2F%2FJWJU8fk%2FVD4UL6QT134zeGUHKQWBZ5PcLfTDSCyy4aKct%2FSQpHQg%2BuyiwGsj0qyh3Ih%2B4SFJ3eBtf446nNAKop8mhSZNN313ZoAUbqVUvkiS2YhoCAN%2FNp0C%2FzDGMwl2Olgz3L%2FSuq7CqeRhT4s3sk6%2B6OiAtK3ZD2ZoXlcicnW3sTffFRAt0tz8vw4bDEH4atH1BgxM7tx0Ifk5LRVtXpvIn837fNts4Zcr7xva3Yc1pJstqenV7dZuDhQQJi01CIO%2F8HdZieo%2FSh11MxjG6%2BfhSjJoXT5ZpmVf7NScBmvLNskh22j12ubNlyUDcIlBwUPj8G1FAS8e3MotNdAZ9oln%2BqxeE%2F7K0kuUSdU8kq9N5ZZd8Vx3m9OLi%2BobRZ5acVpT3TvtJMA2DNf9OGvC6y4eTrN9n3YoIMqvu8593mcW5fTIEgH7OjJV6UBoqH2c%2BjiNi1ZZsK9o6KlESd8i4v8C2DF52mBlTIG0tYkXI5KRom9ADo%2Bg%2F%2BGsCAiJPOHedlOBRsro%2BQ3iV6tjbgad2LwF2azWYCQBdmwSk3UJFpeHUc6cUvhMcy0BJ5tKE

    and from Web.Host Logs I get DEBUG 2022-11-09 09:16:49,918 [29 ] Abp.AspNetCore.SignalR.Hubs.AbpCommonHub - A client is connected: {"ConnectionId":"OT1w2usalDZHMcb4OoyyCQ","IpAddress":"127.0.0.1","TenantId":1,"UserId":3,"ConnectTime":"2022-11-09T09:16:49.9163277+02:00","Properties":{}}

    I send the notification using the below function in AppNotifier when I create a new object

        public async Task NewIncidentAsync(Incident incident)
        {
            var notificationData = new MessageNotificationData("New " + incident.Criticality.ToString() + " " + 
                incident.Type.ToString() + " incident:" + incident.Description);
    
            notificationData["description"] = incident.Description;
            notificationData["type"] = incident.Type.ToString();
            notificationData["critical"] = incident.Criticality.ToString();
            notificationData["lat"] = incident.Latitude;
            notificationData["lon"] = incident.Longitude;
            notificationData["id"] = incident.Id;
    
            await _notificationPublisher.PublishAsync(AppNotificationNames.NewIncident, notificationData,severity: NotificationSeverity.Warn);
        }
    

    but maybe I do something wrong in message subscription code.

    HubConnection.On<string>(AppNotificationNames.NewIncident, (message) => { Console.WriteLine("TEST"); });

    Thank you!

  • 0
    ismcagdas created
    Support Team

    Hi,

    Thanks, running the app using Mobile profile is also fine. Could you also share your Hub configuration in your Startup.cs file ?

  • 0
    mgc created

    Hi,

    I have not change anything else about hub configuration. In Web.Host Startup.cs I have the default code. app.UseEndpoints(endpoints => { endpoints.MapHub<AbpCommonHub>("/signalr"); endpoints.MapHub<ChatHub>("/signalr-chat"); ... I may not have understood something correctly about how Notifications work. I have not implemented a separate hub. Do the notifications send a message to the AbpCommonHub signalr hub? If there was a sample or some code on how i can get them in xamarin application that would be great.

    Thank you!

  • 0
    ismcagdas created
    Support Team

    Hi @mgc

    Sorry for my late reply. Actaully your approach seems fine. But, could you create a new hub similar to endpoints.MapHub<ChatHub>("/signalr-chat"); and use it in your Xamarin app as well and send the notification using this hub similar to https://github.com/aspnetzero/aspnet-zero-core/blob/dev/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Web.Core/Chat/SignalR/SignalRChatCommunicator.cs#L46 and see if it works ?

  • 0
    mgc created

    Hi,

    I created a new separate hub with a new signalr communicator similar to SignalRChatCommunicator. I modified the Startup with the new endpoint of the new hub. Also I created a new console application in order to test the code and subscribe to then event of the new hub. I manage to connect to the new hub (I checked the log for the connection id) running Host in Mobile profile but I cannot get the message.

    I send a message to all clients of the hub using the code below await _incHub.Clients.All.SendAsync("getIncidentMessage", "test");

    The code of the console application is `var hubConnection = new HubConnectionBuilder() .WithUrl("https://localhost:44301/inchub", (opts) => { opts.HttpMessageHandlerFactory = (message) => { if (message is HttpClientHandler clientHandler) // bypass SSL certificate clientHandler.ServerCertificateCustomValidationCallback += (sender, certificate, chain, sslPolicyErrors) => { return true; }; return message; }; } ) .Build();

    hubConnection.On("getMessage", message => Console.WriteLine(message)); hubConnection.On("getIncidentMessage", message => Console.WriteLine(message)); hubConnection.On("App.NewIncident", message => Console.WriteLine(message)); hubConnection.StartAsync().Wait();`

    Could you please provide a sample code like the code above that works with any message in default hub or chathub of the asp.net zero. If I had the implementation in Xamarin would be perfect.

    I cannot find a solution that works.

    Thank you one more time for your help.

  • 0
    ismcagdas created
    Support Team

    Hi @mgc

    Thanks for the update. We will check it deeply and inform you.

  • 0
    m.aliozkaya created
    Support Team

    Hi @mgc

    I tried to subscribe getChatMessage at signalr-chat hub. I can send and receive messages from Angular to Xamarin app. I hope this code helps to you.

        public async Task&lt;bool&gt; ConnectAsync()
        {
            ChatConnection.On&lt;ChatMessageDto&gt;("getChatMessage", (result) =>
            {
                Console.WriteLine("Helo");
                
            });
    
            try
            {
                if (ChatConnection.State == HubConnectionState.Connected)
                {
                    return true;
                }
    
                if (_isConnecting)
                {
                    return false;
                }
    
                _isConnecting = true;
    
                await ChatConnection.StartAsync();
    
                Console.WriteLine(@"Connected!");
    
                return true;
            }
            catch (Exception ex)
            {
                ExceptionHandler.LogException(ex);
                return false;
            }
            finally
            {
                _isConnecting = false;
            }
        }
    
        public async Task&lt;bool&gt; SendMessageAsync(string message)
        {
            if (!await ConnectAsync())
            {
                return false;
            }
    
            try
            {
                var messageData = new SendChatMessageInput
                {
                    TenantId = 1,
                    UserId = 2,
                    Message = message,
                };
    
                var response = await ChatConnection.InvokeAsync&lt;string&gt;("SendMessage", messageData);
    
                if (string.IsNullOrEmpty(response))
                {
                    Console.WriteLine(@"Message successfully sent!");
                    return true;
                }
    
                Console.WriteLine(@"Message failed to send: " + response);
                return true;
            }
            catch (Exception ex)
            {
                ExceptionHandler.LogException(ex);
                return false;
            }
        }
    
  • 0
    mgc created

    Hi m.aliozkaya,

    Thank you very much for your help and your code.

    I used the chathub and your code and I managed to send a message from xamarin to web mvc application and at the same time the On<ChatMessageDto> was called when I sent the message from Xamarin.

    But when I send a chat message from the mvc web application then the xamarin application does not receive it. On web mvc application I can send a chat message from one user to another.

    Please note that I do not use Angular but MVC.

    Any help would be appreciated. Thank you once more!

  • 0
    ismcagdas created
    Support Team

    Hi @mgc

    Is your final CreateHubUrl contains enc_auth_token query string parameter with a valid encrpyed token ?

  • 0
    mgc created

    Hi ismcagdas,

    Yes, the CreateHubUrl contains the enc_auth_token. Please see the screenshot below from the Host log.

    I can send the message successfully from Xamarin to Web app but I cannot receive a message from Web app.

    This is my code for the notification service:

    public class NotificationService : ISingletonDependency, INotificationService
        {
            public HubConnection _HubConnection { get; }
    
            private readonly IAccessTokenManager _accessTokenManager;
            private bool _isConnecting;
    
            public NotificationService(IAccessTokenManager accessTokenManager)
            {
                try
                {
                    _accessTokenManager = accessTokenManager;
    
                    _HubConnection = new HubConnectionBuilder()
                        .WithUrl(CreateHubUrl(),
    
                        (opts) =>
                            {
                                opts.HttpMessageHandlerFactory = (message) =>
                                {
                                    if (message is HttpClientHandler clientHandler)
                                        // bypass SSL certificate
                                        clientHandler.ServerCertificateCustomValidationCallback +=
                                            (sender, certificate, chain, sslPolicyErrors) => { return true; };
                                    return message;
                                };
                            }
                        )
                        .Build();
    
    
                    _HubConnection.Closed += TryToConnectIfNeeds;
    
                }
                catch (Exception ex)
                {
                    ExceptionHandler.LogException(ex);
                }
            }
    
            private async Task TryToConnectIfNeeds(Exception connectionException)
            {
                try
                {
                    ExceptionHandler.LogException(connectionException);
    
                    if (!_accessTokenManager.IsUserLoggedIn)
                    {
                        return;
                    }
    
                    await Task.Delay(new Random().Next(0, 5) * 1000);
                    await _HubConnection.StartAsync();
                }
                catch (Exception ex)
                {
                    ExceptionHandler.LogException(ex);
                }
            }
    
            public async Task<bool> SendMessageAsync(string message)
            {
                if (!await ConnectAsync())
                {
                    return false;
                }
    
                try
                {
                    //find an existing user, I picked the user with Id:4
                    var messageData = new SendChatMessageInput
                    {
                        TenantId = 1,
                        TenancyName = "Default",
                        UserId = 2,
                        UserName = "admin",
                        Message = message
                    };
    
                    var response = await _HubConnection.InvokeAsync<string>("SendMessage", messageData);
                    if (string.IsNullOrEmpty(response))
                    {
                        Console.WriteLine(@"Message successfully sent!");
                        return true;
                    }
    
                    Console.WriteLine(@"Message failed to send: " + response);
                    return true;
                }
                catch (Exception ex)
                {
                    ExceptionHandler.LogException(ex);
                    return false;
                }
            }
    
    
    
            public async Task<bool> ConnectAsync()
            {
                _HubConnection.On<ChatMessageDto>("getChatMessage", (result) =>
                {
                    Console.WriteLine("Helo");
    
                });
    
                try
                {
                    if (_HubConnection.State == HubConnectionState.Connected)
                    {
                        return true;
                    }
    
                    if (_isConnecting)
                    {
                        return false;
                    }
    
                    _isConnecting = true;
    
                    await _HubConnection.StartAsync();
    
                    Console.WriteLine(@"Connected!");
                    return true;
                }
                catch (Exception ex)
                {
                    ExceptionHandler.LogException(ex);
                    return false;
                }
                finally
                {
                    _isConnecting = false;
                }
            }
    
    
            public async Task<bool> DisconnectAsync()
            {
                try
                {
                    if (_HubConnection.State == HubConnectionState.Disconnected)
                    {
                        return true;
                    }
    
                    await _HubConnection.StopAsync();
    
                    Console.WriteLine(@"Disconnected!");
                    return true;
                }
                catch (Exception ex)
                {
                    ExceptionHandler.LogException(ex);
                    return false;
                }
            }
    
            private string CreateHubUrl()
            {
                string accessToken = _accessTokenManager.AuthenticateResult.EncryptedAccessToken;
                return ApiUrlConfig.BaseUrl +
                       AppConsts.SignalRChatHubPath.TrimStart('/') + "?" +
                       AppConsts.EncryptedAuthTokenQueryStringName + "=" +
                       Uri.EscapeDataString(accessToken);
            }
        }
    

    Then inside a view model using a button I call the SendMessageAsync. I might be doing something wrong in how I call and what I call in the class NotificationService above.

    Thank you very much for your help!

  • 0
    ismcagdas created
    Support Team

    Hi @mgc

    I think this happens because the default MVC template doesn't handle enc_auth_token query string parameter. Could you add this line to your MVC AuthConfigurer class https://github.com/aspnetzero/aspnet-zero-core/blob/dev/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Web.Host/Startup/AuthConfigurer.cs#L57 and also implement QueryStringTokenResolver in your MVC app ?

  • 0
    mgc created

    Hi ismcagdas,

    The above code exists in the Host AuthConfigurer class.

    Could you please have a look at the code in the project I am sharing with you in the link below?

    https://mgcodegr.sharepoint.com/:u:/g/Ea22k-KFFlVNvWuhI6EnKk8BalPc2UlLMpV1XzjOlS3p_w?e=JpqahC

    I have created in the mobile shared the service NotificationService and in the IncidentsViewModel I call the SendMessage which successfully sends the message and the web app receives it but I am not able to receive a message when I send it from the web app.

    Thank you very much for your time and your help!

  • 0
    m.aliozkaya created
    Support Team

    Hi @mgc ,

    We are working on it, we will try to give an answer as soon as possible