如何在 Blazor Web 程序集中检查客户端连接状态

Ali*_*Ali 6 c# signalr asp.net-web-api asp.net-core blazor-webassembly

我有一个 Blazor webasemmbly 应用程序,它使用 asp.net core 作为后端,使用 Blazor wasm 作为前端。我有一个类可以检查 HTTP 问题,例如 notfound、BadReqest 和...

  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);
            }
        }       
    }
Run Code Online (Sandbox Code Playgroud)

这个 HTTP 拦截器做得很好,但问题是当客户端应用程序 (wasm) 失去与服务器的连接时(由于任何原因,如没有互联网,或服务器停止运行......),它不起作用,并且不会有用的。当服务器运行得不好时。

所以搜索后我发现我们必须检查 signalR 连接状态,但我找不到有关如何实现它的示例或教程。

我想将其全局添加到应用程序中。

ssa*_*mko 6

检查互联网是否可用的 SignalR 方式有很多缺点。

  1. 您无法区分丢失的互联网连接或只是无法访问的服务器。
  2. 通常,与服务器的并发连接数量有限,这种方法在我看来是一种资源浪费。

还有一种方法可以通过一段JS代码来判断用户是否离线:

window.navigator.onLine
Run Code Online (Sandbox Code Playgroud)

更多讨论这里:检测互联网连接是否离线? 但通过这种方法,您应该能够了解用户离线时的大多数情况。

如何将其实现到您的代码中?创建一个名为Connection.razor的组件并将其放在那里:

@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");
    }

}
Run Code Online (Sandbox Code Playgroud)

然后创建一个JS文件Connection.js,代码如下:

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);
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

然后将此 JS 链接到您的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>
Run Code Online (Sandbox Code Playgroud)

最后在MainLayout.razor中使用此组件,例如:

<Connection>
    <Online>
        <h1 style="color: green">Online</h1>
    </Online>
    <Offline>
        <h1 style="color: red">Offline</h1>
    </Offline>
</Connection>
Run Code Online (Sandbox Code Playgroud)

通过这种方法,您可以通知用户如果其连接不可用。

来源:

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/
Run Code Online (Sandbox Code Playgroud)


Nei*_*l W 5

首先,在您的服务器项目中创建一个集线器。

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

启动.cs

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

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

我的中心

public class MyHub : Hub
{
}
Run Code Online (Sandbox Code Playgroud)

然后,在您的 WASM 客户端中创建一个服务来管理集线器连接:

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.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在Program.cs中注册服务

builder.Services.AddSingleton<SignalRClientService>();
Run Code Online (Sandbox Code Playgroud)