我们有一位客户正在使用自定义浏览器,这不允许他们打开控制台来查看记录的错误消息。不幸的是,我们无法访问他们的系统,因此我们需要以某种方式获取异常消息和堆栈跟踪。
我尝试构建自定义 ErrorBoundary (https://blazorschool.com/tutorial/blazor-server/dotnet7/error-handling-101402),但问题是,这只会显示错误消息或内容,即使我渲染了两者:
自定义错误边界.cs:
...
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (ErrorContent is not null)
{
builder.AddContent(0, ErrorContent(CurrentException));
}
builder.AddContent(1, ChildContent);
}
...
App.razor:
<CustomErrorBoundary @ref="@errorBoundary">
<ChildContent>
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<Unauthorized></Unauthorized>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<AppPageTitle PageTitle="@Frontend.ErrotPage_NotFoundTitle" />
<LayoutView Layout="@typeof(MainLayout)">
<MudAlert Severity="Severity.Error" Variant="Variant.Filled" Square="true" Class="ma-2">@Frontend.ErrorPage_NotFound</MudAlert>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
</ChildContent>
<ErrorContent Context="Exception">
<p>An error occured.</p>
@if (Exception != null)
{
<p>@Exception.Message</p>
}
</ErrorContent>
</CustomErrorBoundary>
我需要的是使用一个按钮来扩展默认的 blazor 错误消息(“发生了未处理的错误。重新加载”)以展开异常消息和堆栈跟踪。据我了解这是不可能的,因为它在index.html文件中。
有没有办法在出现异常的情况下仍然显示内容并让用户继续使用网站并显示异常详细信息?
我知道我可以设置记录器将日志发送到 API,但在连接到 API 之前可能会发生异常。
我通过扩展这个答案找到了解决方案https://stackoverflow.com/a/63715234/11385442。
在index.html中我添加了一个新元素“error-detail”:
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
<span id="error-detail"></span>
</div>
UnhandledExceptionLogger 现在正在接收对 UnhandledExceptionProvider 的引用,它定义了一个新的 Action,每当发生未处理的异常时就会调用该操作:
public class UnhandledExceptionProvider : ILoggerProvider
{
UnhandledExceptionSender _unhandledExceptionSender;
public Action<LogLevel, Exception> OnLog { get; set; }
public UnhandledExceptionProvider(UnhandledExceptionSender unhandledExceptionSender)
{
_unhandledExceptionSender = unhandledExceptionSender;
}
public ILogger CreateLogger(string categoryName)
{
return new UnhandledExceptionLogger(categoryName, _unhandledExceptionSender, this);
}
public void Dispose()
{
}
public class UnhandledExceptionLogger : ILogger
{
private readonly string _categoryName;
private readonly UnhandledExceptionSender _unhandeledExceptionSender;
private readonly UnhandledExceptionProvider _unhandledExceptionProvider;
public UnhandledExceptionLogger(string categoryName, UnhandledExceptionSender unhandledExceptionSender, UnhandledExceptionProvider unhandledExceptionProvider)
{
_unhandeledExceptionSender = unhandledExceptionSender;
_categoryName = categoryName;
_unhandledExceptionProvider = unhandledExceptionProvider;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
// Unhandled exceptions will call this method
_unhandledExceptionProvider.OnLog?.Invoke(logLevel, exception);
}
public IDisposable BeginScope<TState>(TState state)
{
return new NoopDisposable();
}
private class NoopDisposable : IDisposable
{
public void Dispose()
{
}
}
}
}
现在在 Program.cs 中,我可以使用 Javascript 在 DOM 中设置错误详细信息:
public static async Task Main(string[] args)
{
...
var unhandledExceptionSender = new UnhandledExceptionSender();
var unhandledExceptionProvider = new UnhandledExceptionProvider(unhandledExceptionSender);
builder.Logging.AddProvider(unhandledExceptionProvider);
builder.Services.AddSingleton<IUnhandledExceptionSender>(unhandledExceptionSender);
WebAssemblyHost host = builder.Build();
unhandledExceptionProvider.OnLog = (LogLevel, exception) =>
{
if (logLevel == LogLevel.Critical && exception != null)
{
var jsRuntime = host.Services.GetRequiredService<IJSRuntime>();
string? stackTrace = exception.StackTrace != null ?
Encoding.UTF8.GetString(Encoding.UTF32.GetBytes(exception.StackTrace)) :
string.Empty;
string errorDetail = (exception.Message + "<br>" + stackTrace)
.Replace("\n", "<br>");
jsRuntime.InvokeVoidAsync("eval", $"document.getElementById('error-detail').innerHTML='{errorDetail}';");
}
};
await host.RunAsync();
}