You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Blazor Server中全局捕获未处理异常并获取浏览器当前URL的实现方案

全局捕获Blazor Server未处理异常并记录当前浏览器URL

我完全理解你的痛点——Blazor Server用SignalR通信后,Serilog请求日志只能拿到初始的/_blazor路径,根本没法定位异常发生在哪个页面路由上。而且手动加try-catch太繁琐,还会破坏代码结构。下面给你几个不用写大量try-catch就能全局捕获异常并拿到当前URL的方案:

方案1:用全局ErrorBoundary直接处理

Blazor的ErrorBoundary是组件级的异常捕获容器,把它套在根组件外面,就能捕获整个应用中组件渲染、事件触发时的未处理异常,同时结合NavigationManager轻松拿到当前浏览器的路由URL。

实现步骤:

打开App.razor,注入NavigationManagerILogger,用ErrorBoundary包裹整个路由组件,自定义错误处理逻辑:

@inject NavigationManager NavManager
@inject ILogger<App> Logger

<ErrorBoundary OnError="HandleGlobalError">
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
            <FocusOnNavigate RouteData="@routeData" Selector="h1" />
        </Found>
        <NotFound>
            <PageTitle>Not found</PageTitle>
            <LayoutView Layout="@typeof(MainLayout)">
                <p role="alert">Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</ErrorBoundary>

@code {
    private void HandleGlobalError(Exception exception)
    {
        // 拿到当前浏览器的完整URL,包括Blazor路由
        var currentPageUrl = NavManager.Uri;
        // 用Serilog记录异常和URL
        Logger.LogError(exception, "未处理异常发生在页面: {CurrentUrl}", currentPageUrl);
        
        // 这里不用额外处理错误UI,Blazor会自动显示默认的blazor-error-ui
        // 如果想自定义错误页面,可以在这里添加逻辑
    }
}

这个方案简单直接,能覆盖绝大多数组件层面的异常,而且完全不需要修改业务代码。

方案2:注册全局异常日志服务(更优雅的全局处理)

如果想让所有ErrorBoundary都统一处理异常日志,可以实现IErrorBoundaryLogger接口,把日志逻辑抽成全局服务,这样不管哪里用ErrorBoundary,都会自动触发日志记录。

实现步骤:

  1. 创建自定义的异常日志服务:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Logging;

public class GlobalErrorLogger : IErrorBoundaryLogger
{
    private readonly ILogger<GlobalErrorLogger> _logger;
    private readonly NavigationManager _navManager;

    // 构造函数注入需要的服务
    public GlobalErrorLogger(ILogger<GlobalErrorLogger> logger, NavigationManager navManager)
    {
        _logger = logger;
        _navManager = navManager;
    }

    public ValueTask LogErrorAsync(Exception exception, ErrorBoundaryErrorLogContext context)
    {
        var currentUrl = _navManager.Uri;
        _logger.LogError(exception, "全局未处理异常 | 当前页面: {CurrentUrl}", currentUrl);
        return ValueTask.CompletedTask;
    }
}
  1. Program.cs中注册这个服务:
builder.Services.AddScoped<IErrorBoundaryLogger, GlobalErrorLogger>();
  1. 然后在App.razor中用CascadingErrorBoundary包裹整个应用(这样所有子组件的ErrorBoundary都会继承这个全局日志逻辑):
<CascadingErrorBoundary>
    <Router AppAssembly="@typeof(App).Assembly">
        <!-- 路由内容和之前一致 -->
    </Router>
</CascadingErrorBoundary>

这个方案的优势是代码解耦,日志逻辑集中管理,后续要修改日志格式或添加其他处理只需要改这一个类。

方案3:处理SignalR连接类的异常(可选)

如果还想捕获SignalR连接中断、电路关闭这类组件之外的异常,可以自定义CircuitHandler来处理:

实现步骤:

  1. 创建自定义CircuitHandler:
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.Logging;

public class CustomCircuitHandler : CircuitHandler
{
    private readonly ILogger<CustomCircuitHandler> _logger;
    private readonly NavigationManager _navManager;

    public CustomCircuitHandler(ILogger<CustomCircuitHandler> logger, NavigationManager navManager)
    {
        _logger = logger;
        _navManager = navManager;
    }

    public override Task OnConnectionDownAsync(Circuit circuit, CancellationToken cancellationToken)
    {
        var currentUrl = _navManager.Uri;
        _logger.LogWarning("SignalR连接中断 | 当前页面: {CurrentUrl}", currentUrl);
        return base.OnConnectionDownAsync(circuit, cancellationToken);
    }

    public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken)
    {
        var currentUrl = _navManager.Uri;
        _logger.LogInformation("用户会话关闭 | 当前页面: {CurrentUrl}", currentUrl);
        return base.OnCircuitClosedAsync(circuit, cancellationToken);
    }
}
  1. Program.cs注册:
builder.Services.AddScoped<CircuitHandler, CustomCircuitHandler>();

关键提示

  • NavigationManager.Uri是核心:它会同步浏览器的当前路由,返回的是用户实际看到的URL(比如/counter),而不是SignalR的/_blazor路径,完全满足你的需求。
  • 这些方案都不会影响Blazor默认的blazor-error-ui显示,异常发生后用户依然能看到默认的错误提示。
  • 不需要在任何业务组件中写try-catch,全局处理就能覆盖绝大多数场景。

内容的提问来源于stack exchange,提问作者Dejan Dular

火山引擎 最新活动