У нас есть клиент, который использует собственный браузер, который не позволяет ему открывать консоль для просмотра зарегистрированных сообщений об ошибках. К сожалению, у нас нет доступа к их системе, поэтому нам нужно каким-то образом получить сообщение об исключении и трассировку стека.
Я пытался создать собственную ErrorBoundary (https://blazorschool.com/tutorial/blazor-server/dotnet7/error-handling-101402), но проблема в том, что при этом будет отображаться только сообщение об ошибке или содержимое. , хотя я визуализирую оба:
CustomErrorBoundary.cs:
...
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (ErrorContent is not null)
{
builder.AddContent(0, ErrorContent(CurrentException));
}
builder.AddContent(1, ChildContent);
}
...
Приложение.бритва:
<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, который содержит событие, которое вызывается всякий раз, когда возникает необработанное исключение:
public class UnhandledExceptionProvider : ILoggerProvider
{
public event Action<LogLevel, Exception?>? Log;
public UnhandledExceptionProvider()
{
}
public ILogger CreateLogger(string categoryName)
{
return new UnhandledExceptionLogger(this);
}
public void Dispose()
{
}
private class UnhandledExceptionLogger : ILogger
{
private readonly UnhandledExceptionProvider unhandledExceptionProvider;
public UnhandledExceptionLogger(UnhandledExceptionProvider unhandledExceptionProvider)
{
this.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)
where TState : notnull
{
return new EmptyDisposable();
}
private class EmptyDisposable : 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.Log += (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>" + HttpUtility.HtmlEncode(stackTrace).Replace(@"\", @"\\").Replace("\n", "<br>");
jsRuntime.InvokeVoidAsync("eval", $"document.getElementById('error-detail').innerHTML='{errorDetail}';");
}
};
await host.RunAsync();
}
Я также написал об этом статью: https://ben-5.azurewebsites.net/2024/4/17/display-unhandled-client-Exceptions-with-blazor/
В вашем решении в этом посте отсутствует класс
UnhandledExceptionSender
, но используется его экземпляр во время настройки компоновщика в программе. Это упущение? Каким путем вы собираетесь это сделать, с Отправителем или без него?