'how to Check Client connection status in Blazor web assembly

I have a Blazor webasemmbly app, it's using asp.net core as backend and Blazor wasm as frontend. I have a class that can check the HTTP issues like notfound, BadReqest, and ...

  public class HttpInterceptorService
    {
        private readonly HttpClientInterceptor _interceptor;
        private readonly NavigationManager _navManager;
        
        private readonly RefreshTokenService _refreshTokenService;
        
        public HttpInterceptorService(HttpClientInterceptor interceptor,
            NavigationManager navManager, 
            RefreshTokenService refreshTokenService)
        {
            _interceptor = interceptor;
            _navManager = navManager;               
            _refreshTokenService = refreshTokenService;
        }

        public void RegisterEvent() => _interceptor.AfterSend += HandleResponse;
        public void RegisterBeforeSendEvent() =>
            _interceptor.BeforeSendAsync += InterceptBeforeSendAsync;

        public void DisposeEvent()
        {
            _interceptor.AfterSend -= HandleResponse;
            _interceptor.BeforeSendAsync -= InterceptBeforeSendAsync;
        }

        private async Task InterceptBeforeSendAsync(object sender,
            HttpClientInterceptorEventArgs e)
        {
            var absolutePath = e.Request.RequestUri.AbsolutePath;

            if (!absolutePath.Contains("token") && !absolutePath.Contains("account"))
            {
                var token = await _refreshTokenService.TryRefreshToken();
                if (!string.IsNullOrEmpty(token))
                {
                    e.Request.Headers.Authorization =
                        new AuthenticationHeaderValue("bearer", token);
                }
            }
        }

        private void HandleResponse(object sender, HttpClientInterceptorEventArgs e)
        {
            if (e.Response == null)
            {
                _navManager.NavigateTo("/PageError");
                throw new HttpResponseException("Server not available.");
            }

            var message = "";

            if (!e.Response.IsSuccessStatusCode)
            {
                
                switch (e.Response.StatusCode)
                {
                    case HttpStatusCode.NotFound:
                        _navManager.NavigateTo("/Page404");                 
                        break;
                    case HttpStatusCode.BadRequest:                                 
                        break;
                    case HttpStatusCode.Unauthorized:
                        _navManager.NavigateTo("/unauthorized");                    
                        break;
                    default:
                        _navManager.NavigateTo("/PageError");                   
                        break;
                }

                throw new HttpResponseException(message);
            }
        }       
    }

this HTTP Interceptor does a great job, but the issue is when the client app (wasm) loses the connection to the server (for any reason like no internet, or server stop running and ...), it doesn't work and is not going to be useful. when the server doesn't run as well .

so searched I found that we have to check the signalR connection status but I couldn't find an example or tutorial on how to implement that.

I want to add it globally to the app.



Solution 1:[1]

Firstly, create a hub in your server project.

https://docs.microsoft.com/en-us/aspnet/core/signalr/hubs?view=aspnetcore-5.0

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
}

public void Configure(IApplicationBuilder app)
{
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<MyHub>("/myhub");
    });
}

MyHub

public class MyHub : Hub
{
}

Then, in your WASM client create a service to manage hub connection:

public class SignalRClientService
{
    HubConnection MyHubConnection;
    NavigationManager NavigationManager;

    public SignalRClientService(NavigationManager navigationManager)
    {
        NavigationManager = navigationManager;

        OpenHub();
    }

    public void OpenHub()
    {
        MyHubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/myhub")
            .Build();

        MyHubConnection.Closed += async (error) =>
        {
            // Do what you need to do ...
            // e.g. 1) Inject this service into your razor component
            //      2) Raise an event from here that connection closed
            //      3) Listen for event in razor component
            //      4) Tell user that connection is closed.

            // You could then try to reinitialize the connection here
            // and raise and event that connection is reestablished.
        }
    }
}

Register service in Program.cs

builder.Services.AddSingleton<SignalRClientService>();

Solution 2:[2]

The SingnalR way of checking if the internet is available brings many disadvantages.

  1. You dont distinguish between lost internet connection or just unreachable server.
  2. You have usually limited amount of concurrent connections to your server and this approach seems to me like a waste of resources.

There is also another way how to know if user is offline by using a piece of JS code:

window.navigator.onLine

More discussed here: Detect the Internet connection is offline? But with this approach you should be able to know most cases when user is offline.

How to implement this into your code ? Create a component named Connection.razor and put there:

@inject IJSRuntime _jsRuntime;
@implements IAsyncDisposable

@if (IsOnline)
{
    @Online
}
else
{
    @Offline
}

@code {

    [Parameter]
    public RenderFragment Online { get; set; }

    [Parameter]
    public RenderFragment Offline { get; set; }

    public bool IsOnline { get; set; }

    [JSInvokable("Connection.StatusChanged")]
    public void OnConnectionStatusChanged(bool isOnline)
    {
        if (IsOnline != isOnline)
        {
            IsOnline = isOnline;
        }

        StateHasChanged();
    }

    protected override async Task OnInitializedAsync() {
        await base.OnInitializedAsync();

        await _jsRuntime.InvokeVoidAsync("Connection.Initialize", DotNetObjectReference.Create(this));
    }

    public async ValueTask DisposeAsync() {
        await _jsRuntime.InvokeVoidAsync("Connection.Dispose");
    }

}

Then create a JS file Connection.js with code:

let handler;

window.Connection = {
    Initialize: function (interop) {

        handler = function () {
            interop.invokeMethodAsync("Connection.StatusChanged", navigator.onLine);
        }

        window.addEventListener("online", handler);
        window.addEventListener("offline", handler);

        handler(navigator.onLine);
    },
    Dispose: function () {

        if (handler != null) {

            window.removeEventListener("online", handler);
            window.removeEventListener("offline", handler);
        }
    }
};

then link this JS inside your index.html:

<body>
    <div id="app" style="height: 100%;">Loading...</div>
    <script src="_framework/blazor.webassembly.js"></script>
    <script src="js/Connection.js"></script>
</body>

and finally use this component for example inside your MainLayout.razor like:

<Connection>
    <Online>
        <h1 style="color: green">Online</h1>
    </Online>
    <Offline>
        <h1 style="color: red">Offline</h1>
    </Offline>
</Connection>

and thanks to this approach you are able to inform user if his connection is unavailable.

Src:

https://www.patrickrobin.co.uk/articles/showing-connection-status-in-blazor-webassembly/
https://www.neptuo.com/blog/2019/12/blazor-network-status/
https://code-maze.com/use-browser-functionalities-with-blazor-webassembly/

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Neil W
Solution 2