Skip to content

Commit e621359

Browse files
authored
feat(ErrorLogger): support native html element throw exception (#7493)
* feat(Tab): 增加 ErrorLogger 相关参数 * refactor: 移除开发模式判断 * refactor: 更改回调接口 * refactor: 增加内部方法 GetLastOrDefaultHandler * refactor: 增加级联参数 IErrorLogger * refactor: 更改渲染逻辑支持原生组件 * refactor: 更新代码 * refactor: 更改为 LastOrDefault 方法 * refactor: 优化代码 * refactor: 优化代码 * refactor: 增加组件周期异常判断 * refactor: 更新代码 * refactor: 增加组件生命周期判断 * refactor: 增加生命周期报错支持 * test: 增加单元测试 * test: 更新单元测试 * refactor: 移除不使用的级联参数 * refactor: 优化代码 * test: 更新单元测试 * test: 更新单元测试 * test: 更新单元测试 * test: 更新单元测试
1 parent 444c735 commit e621359

6 files changed

Lines changed: 288 additions & 57 deletions

File tree

src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ class BootstrapBlazorErrorBoundary : ErrorBoundaryBase
5656
[NotNull]
5757
public string? ToastTitle { get; set; }
5858

59+
[CascadingParameter, NotNull]
60+
private IErrorLogger? ErrorLogger { get; set; }
61+
5962
/// <summary>
6063
/// <inheritdoc/>
6164
/// </summary>
@@ -76,29 +79,46 @@ protected override Task OnErrorAsync(Exception exception)
7679
protected override void BuildRenderTree(RenderTreeBuilder builder)
7780
{
7881
// 页面生命周期内异常直接调用这里
82+
var pageException = false;
7983
var ex = CurrentException ?? _exception;
8084
if (ex != null)
8185
{
82-
// 处理自定义异常逻辑
83-
if (OnErrorHandleAsync != null)
84-
{
85-
_ = OnErrorHandleAsync(Logger, ex);
86-
return;
87-
}
88-
89-
// 渲染异常内容
90-
builder.AddContent(0, ExceptionContent(ex));
86+
pageException = IsPageException(ex);
9187

9288
// 重置 CurrentException
9389
ResetException();
90+
91+
// 渲染异常
92+
var handler = GetLastOrDefaultHandler();
93+
_ = RenderException(ex, handler);
9494
}
95-
else
95+
96+
// 判断是否为组件周期内异常
97+
if (!pageException)
9698
{
9799
// 渲染正常内容
98100
builder.AddContent(1, ChildContent);
99101
}
100102
}
101103

104+
private static readonly string[] PageMethods = new string[] { "SetParametersAsync", "RunInitAndSetParametersAsync", "OnAfterRenderAsync" };
105+
106+
private static bool IsPageException(Exception ex)
107+
{
108+
var errorMessage = ex.ToString();
109+
return PageMethods.Any(i => errorMessage.Contains(i, StringComparison.OrdinalIgnoreCase));
110+
}
111+
112+
private IHandlerException? GetLastOrDefaultHandler()
113+
{
114+
IHandlerException? handler = null;
115+
if (ErrorLogger is Components.ErrorLogger logger)
116+
{
117+
handler = logger.GetLastOrDefaultHandler();
118+
}
119+
return handler;
120+
}
121+
102122
private PropertyInfo? _currentExceptionPropertyInfo;
103123

104124
private void ResetException()
@@ -119,10 +139,9 @@ private void ResetException()
119139
}
120140
else
121141
{
122-
var index = 0;
123-
builder.OpenElement(index++, "div");
124-
builder.AddAttribute(index++, "class", "error-stack");
125-
builder.AddContent(index++, GetErrorContentMarkupString(ex));
142+
builder.OpenElement(0, "div");
143+
builder.AddAttribute(1, "class", "error-stack");
144+
builder.AddContent(2, GetErrorContentMarkupString(ex));
126145
builder.CloseElement();
127146
}
128147
};
@@ -144,34 +163,40 @@ private MarkupString GetErrorContentMarkupString(Exception ex)
144163
/// <param name="handler"></param>
145164
public async Task RenderException(Exception exception, IHandlerException? handler)
146165
{
166+
// 记录日志
167+
await OnErrorAsync(exception);
168+
147169
// 外部调用
148170
if (OnErrorHandleAsync != null)
149171
{
150172
await OnErrorHandleAsync(Logger, exception);
151-
return;
152173
}
153174

154-
// 记录日志
155-
await OnErrorAsync(exception);
156-
157175
if (handler != null)
158176
{
159-
// 非开发模式下弹窗提示错误信息
160-
await ToastService.Error(ToastTitle, exception.Message);
161-
return;
177+
// IHandlerException 处理异常逻辑
178+
await handler.HandlerExceptionAsync(exception, ExceptionContent);
179+
}
180+
else
181+
{
182+
// 显示异常信息
183+
await ShowErrorToast(exception);
162184
}
163-
164-
// 显示异常信息
165-
await ShowErrorToast(exception);
166-
_exception = exception;
167-
StateHasChanged();
168185
}
169186

170187
private async Task ShowErrorToast(Exception exception)
171188
{
172189
if (ShowToast)
173190
{
174-
await ToastService.Error(ToastTitle, exception.Message);
191+
var option = new ToastOption()
192+
{
193+
Category = ToastCategory.Error,
194+
Title = ToastTitle,
195+
ChildContent = ErrorContent == null
196+
? ExceptionContent(exception)
197+
: ErrorContent(exception)
198+
};
199+
await ToastService.Show(option);
175200
}
176201
}
177202
}

src/BootstrapBlazor/Components/ErrorLogger/ErrorLogger.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,12 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
133133
/// </summary>
134134
/// <param name="exception"></param>
135135
/// <returns></returns>
136-
public Task HandlerExceptionAsync(Exception exception) => _errorBoundary.RenderException(exception, _cache.LastOrDefault());
136+
public Task HandlerExceptionAsync(Exception exception) => _errorBoundary.RenderException(exception, GetLastOrDefaultHandler());
137137

138138
private readonly List<IHandlerException> _cache = [];
139139

140+
internal IHandlerException? GetLastOrDefaultHandler() => _cache.LastOrDefault();
141+
140142
/// <summary>
141143
/// <inheritdoc/>
142144
/// </summary>

src/BootstrapBlazor/Components/Layout/Layout.razor

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@
149149
RefreshToolbarTooltipText="@RefreshToolbarTooltipText" FullscreenToolbarTooltipText="@FullscreenToolbarTooltipText"
150150
OnToolbarRefreshCallback="OnToolbarRefreshCallback" TabHeader="TabHeader" OnCloseTabItemAsync="OnCloseTabItemAsync"
151151
Body="@Main" NotAuthorized="NotAuthorized!" NotFound="NotFound!" NotFoundTabText="@NotFoundTabText"
152-
EnableErrorLogger="@EnableLogger" ErrorLoggerToastTitle="@ErrorLoggerToastTitle">
152+
EnableErrorLogger="@EnableLogger" EnableErrorLoggerILogger="@EnableErrorLoggerILogger"
153+
ShowErrorLoggerToast="ShowErrorLoggerToast" ErrorLoggerToastTitle="@ErrorLoggerToastTitle"
154+
OnErrorHandleAsync="OnErrorHandleAsync">
153155
</Tab>;
154156

155157
RenderFragment RenderFooter =>

src/BootstrapBlazor/Components/Tab/Tab.razor.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
using Microsoft.AspNetCore.Components.Web;
77
using Microsoft.Extensions.Localization;
8+
using Microsoft.Extensions.Logging;
89
using System.Collections.Concurrent;
910
using System.Reflection;
1011

@@ -442,12 +443,30 @@ public partial class Tab
442443
[Parameter]
443444
public bool? EnableErrorLogger { get; set; }
444445

446+
/// <summary>
447+
/// 获得/设置 是否记录异常到 <see cref="ILogger"/> 默认 null 使用 <see cref="BootstrapBlazorOptions.EnableErrorLoggerILogger"/> 设置值
448+
/// </summary>
449+
[Parameter]
450+
public bool? EnableErrorLoggerILogger { get; set; }
451+
452+
/// <summary>
453+
/// 获得/设置 是否显示 Error 提示弹窗 默认 null 使用 <see cref="BootstrapBlazorOptions.ShowErrorLoggerToast"/> 设置值
454+
/// </summary>
455+
[Parameter]
456+
public bool? ShowErrorLoggerToast { get; set; }
457+
445458
/// <summary>
446459
/// 获得/设置 错误日志 <see cref="Toast"/> 弹窗标题 默认 null
447460
/// </summary>
448461
[Parameter]
449462
public string? ErrorLoggerToastTitle { get; set; }
450463

464+
/// <summary>
465+
/// 获得/设置 自定义错误处理回调方法
466+
/// </summary>
467+
[Parameter]
468+
public Func<ILogger, Exception, Task>? OnErrorHandleAsync { get; set; }
469+
451470
[CascadingParameter]
452471
private Layout? Layout { get; set; }
453472

src/BootstrapBlazor/Components/Tab/TabItemContent.cs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
55

66
using Microsoft.AspNetCore.Components.Rendering;
7+
using Microsoft.Extensions.Configuration;
78

89
namespace BootstrapBlazor.Components;
910

@@ -15,19 +16,24 @@ class TabItemContent : IComponent, IHandlerException, IDisposable
1516
[Parameter, NotNull]
1617
public TabItem? Item { get; set; }
1718

18-
[CascadingParameter]
19-
private Layout? Layout { get; set; }
20-
2119
[CascadingParameter, NotNull]
2220
private Tab? TabSet { get; set; }
2321

2422
[Inject, NotNull]
2523
private DialogService? DialogService { get; set; }
2624

25+
[Inject]
26+
[NotNull]
27+
private ToastService? ToastService { get; set; }
28+
2729
[Inject]
2830
[NotNull]
2931
private IOptionsMonitor<BootstrapBlazorOptions>? Options { get; set; }
3032

33+
[Inject]
34+
[NotNull]
35+
private IConfiguration? Configuration { get; set; }
36+
3137
private IErrorLogger? _logger;
3238

3339
private RenderHandle _renderHandle;
@@ -52,25 +58,27 @@ private void RenderContent()
5258

5359
private Guid _key = Guid.NewGuid();
5460

61+
private bool EnableErrorLogger => TabSet.EnableErrorLogger ?? Options.CurrentValue.EnableErrorLogger;
62+
private bool EnableErrorLoggerILogger => TabSet.EnableErrorLoggerILogger ?? Options.CurrentValue.EnableErrorLoggerILogger;
63+
private bool ShowErrorLoggerToast => TabSet.ShowErrorLoggerToast ?? Options.CurrentValue.ShowErrorLoggerToast;
64+
private string ToastTitle => TabSet.ErrorLoggerToastTitle ?? "Error";
65+
5566
private void BuildRenderTree(RenderTreeBuilder builder)
5667
{
5768
builder.OpenComponent<ErrorLogger>(0);
5869
builder.SetKey(_key);
5970
builder.AddAttribute(1, nameof(ErrorLogger.ChildContent), Item.ChildContent);
60-
61-
var enableErrorLogger = TabSet.EnableErrorLogger ?? Options.CurrentValue.EnableErrorLogger;
62-
builder.AddAttribute(2, nameof(ErrorLogger.EnableErrorLogger), enableErrorLogger);
63-
64-
// TabItem 不需要 Toast 提示错误信息
65-
builder.AddAttribute(3, nameof(ErrorLogger.ShowToast), false);
66-
builder.AddAttribute(4, nameof(ErrorLogger.ToastTitle), TabSet.ErrorLoggerToastTitle);
67-
builder.AddAttribute(5, nameof(ErrorLogger.OnInitializedCallback), new Func<IErrorLogger, Task>(logger =>
71+
builder.AddAttribute(2, nameof(ErrorLogger.EnableErrorLogger), EnableErrorLogger);
72+
builder.AddAttribute(3, nameof(ErrorLogger.EnableILogger), EnableErrorLoggerILogger);
73+
builder.AddAttribute(4, nameof(ErrorLogger.ShowToast), ShowErrorLoggerToast);
74+
builder.AddAttribute(5, nameof(ErrorLogger.ToastTitle), ToastTitle);
75+
builder.AddAttribute(6, nameof(ErrorLogger.OnInitializedCallback), new Func<IErrorLogger, Task>(logger =>
6876
{
6977
_logger = logger;
7078
_logger.Register(this);
7179
return Task.CompletedTask;
7280
}));
73-
builder.AddAttribute(6, nameof(ErrorLogger.OnErrorHandleAsync), Layout?.OnErrorHandleAsync);
81+
builder.AddAttribute(7, nameof(ErrorLogger.OnErrorHandleAsync), TabSet.OnErrorHandleAsync);
7482
builder.CloseComponent();
7583
}
7684

@@ -83,12 +91,31 @@ public void Render()
8391
RenderContent();
8492
}
8593

94+
private bool _detailedErrorsLoaded;
95+
private bool _showDetailedErrors;
8696
/// <summary>
8797
/// <inheritdoc/>
8898
/// </summary>
8999
/// <param name="ex"></param>
90100
/// <param name="errorContent"></param>
91-
public Task HandlerExceptionAsync(Exception ex, RenderFragment<Exception> errorContent) => DialogService.ShowErrorHandlerDialog(errorContent(ex));
101+
public async Task HandlerExceptionAsync(Exception ex, RenderFragment<Exception> errorContent)
102+
{
103+
if (!_detailedErrorsLoaded)
104+
{
105+
_showDetailedErrors = Configuration.GetValue("DetailedErrors", false);
106+
_detailedErrorsLoaded = true;
107+
}
108+
109+
var useDialog = _showDetailedErrors || !ShowErrorLoggerToast;
110+
if (useDialog)
111+
{
112+
await DialogService.ShowErrorHandlerDialog(errorContent(ex));
113+
}
114+
else
115+
{
116+
await ToastService.Error(ToastTitle, ex.Message);
117+
}
118+
}
92119

93120
/// <summary>
94121
/// IDispose 方法用于释放资源

0 commit comments

Comments
 (0)