diff --git a/src/BootstrapBlazor/Components/BaseComponents/BootstrapBlazorRoot.razor b/src/BootstrapBlazor/Components/BaseComponents/BootstrapBlazorRoot.razor index 21f32a9b79e..2b95fa23cf2 100644 --- a/src/BootstrapBlazor/Components/BaseComponents/BootstrapBlazorRoot.razor +++ b/src/BootstrapBlazor/Components/BaseComponents/BootstrapBlazorRoot.razor @@ -2,7 +2,8 @@ @namespace BootstrapBlazor.Components - + @ChildContent diff --git a/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs b/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs index f84021837c2..f9b13ed90b5 100644 --- a/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs +++ b/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs @@ -68,19 +68,27 @@ protected override async Task OnErrorAsync(Exception exception) /// protected override void BuildRenderTree(RenderTreeBuilder builder) { -#if DEBUG - // DEBUG 模式下显示异常堆栈信息到 UI 页面方便开发人员调试 - if (OnErrorHandleAsync == null) + var ex = CurrentException ?? _exception; + if (ex != null) { - var ex = CurrentException ?? _exception; - if (ex != null) + _exception = null; + + // 处理自定义异常逻辑 + if(OnErrorHandleAsync != null) { - _exception = null; - builder.AddContent(0, ExceptionContent(ex)); + // 页面生命周期内异常直接调用这里 + _ = OnErrorHandleAsync(Logger, ex); + return; } + + // 渲染异常内容 + builder.AddContent(0, ExceptionContent(ex)); + } + else + { + // 渲染正常内容 + builder.AddContent(1, ChildContent); } -#endif - builder.AddContent(1, ChildContent); } private Exception? _exception = null; diff --git a/src/BootstrapBlazor/Components/ErrorLogger/ErrorLogger.cs b/src/BootstrapBlazor/Components/ErrorLogger/ErrorLogger.cs index 2a954f2c909..b6b195dd3fe 100644 --- a/src/BootstrapBlazor/Components/ErrorLogger/ErrorLogger.cs +++ b/src/BootstrapBlazor/Components/ErrorLogger/ErrorLogger.cs @@ -56,6 +56,12 @@ public class ErrorLogger : ComponentBase, IErrorLogger [Parameter] public RenderFragment? ErrorContent { get; set; } + /// + /// Gets or sets the callback function to be invoked during initialization. + /// + [Parameter] + public Func? OnInitializedCallback { get; set; } + [NotNull] private BootstrapBlazorErrorBoundary? _errorBoundary = default; @@ -69,6 +75,20 @@ protected override void OnInitialized() ToastTitle ??= Localizer[nameof(ToastTitle)]; } + /// + /// + /// + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + + if (OnInitializedCallback is not null) + { + await OnInitializedCallback(this); + } + } + /// /// /// @@ -97,7 +117,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) }; /// - /// 由接口调用 + /// 由实现 组件实现类调用 /// /// /// diff --git a/src/BootstrapBlazor/Components/Filters/FilterContext.cs b/src/BootstrapBlazor/Components/Filters/FilterContext.cs index 2ae9e7d8f08..eabbe7dba27 100644 --- a/src/BootstrapBlazor/Components/Filters/FilterContext.cs +++ b/src/BootstrapBlazor/Components/Filters/FilterContext.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone -using System.Runtime.CompilerServices; - namespace BootstrapBlazor.Components; /// diff --git a/src/BootstrapBlazor/Components/Layout/Layout.razor b/src/BootstrapBlazor/Components/Layout/Layout.razor index 994cd0dc1a2..a7e689ea1cf 100644 --- a/src/BootstrapBlazor/Components/Layout/Layout.razor +++ b/src/BootstrapBlazor/Components/Layout/Layout.razor @@ -126,7 +126,10 @@ } else { - @HandlerMain() + + @HandlerMain() + } ; diff --git a/src/BootstrapBlazor/Components/Layout/Layout.razor.cs b/src/BootstrapBlazor/Components/Layout/Layout.razor.cs index b0b09ba18ae..191a13a232f 100644 --- a/src/BootstrapBlazor/Components/Layout/Layout.razor.cs +++ b/src/BootstrapBlazor/Components/Layout/Layout.razor.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Routing; using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; using System.Reflection; namespace BootstrapBlazor.Components; @@ -452,12 +453,40 @@ public partial class Layout : IHandlerException, ITabHeader [Parameter] public object? Resource { get; set; } + /// + /// 获得/设置 是否开启全局异常捕获 默认 null 读取配置文件 EnableErrorLogger 值 + /// + [Parameter] + public bool? EnableErrorLogger { get; set; } + + /// + /// 获得/设置 是否显示 Error 提示弹窗 默认 null 使用 设置值 + /// + [Parameter] + public bool? ShowErrorLoggerToast { get; set; } + + /// + /// 获得/设置 错误日志 弹窗标题 默认 null + /// + [Parameter] + public string? ErrorLoggerToastTitle { get; set; } + + /// + /// 获得/设置 自定义错误处理回调方法 + /// + [Parameter] + public Func? OnErrorHandleAsync { get; set; } + /// /// 获得 登录授权信息 /// [CascadingParameter] private Task? AuthenticationStateTask { get; set; } + [Inject] + [NotNull] + private IOptionsMonitor? Options { get; set; } + [Inject, NotNull] private IServiceProvider? ServiceProvider { get; set; } @@ -470,6 +499,10 @@ public partial class Layout : IHandlerException, ITabHeader private ITabHeader? TabHeader => ShowTabInHeader ? this : null; + private bool _enableErrorLoggerValue => EnableErrorLogger ?? Options.CurrentValue.EnableErrorLogger; + + private bool _showToast => ShowErrorLoggerToast ?? Options.CurrentValue.ShowErrorLoggerToast; + /// /// /// @@ -625,6 +658,15 @@ private async Task ToggleSidebar() await TriggerCollapseChanged(); } + private ErrorLogger? _errorLogger; + + private Task OnErrorLoggerInitialized(ErrorLogger logger) + { + _errorLogger = logger; + _errorLogger.Register(this); + return Task.CompletedTask; + } + /// /// 上次渲染错误内容 /// @@ -678,6 +720,7 @@ protected override async ValueTask DisposeAsync(bool disposing) if (disposing) { + _errorLogger?.UnRegister(this); ErrorLogger?.UnRegister(this); if (SubscribedLocationChangedEvent) { diff --git a/test/UnitTest/Components/ErrorHandlerTest.cs b/test/UnitTest/Components/ErrorHandlerTest.cs index d604b06e228..c031c1ccac9 100644 --- a/test/UnitTest/Components/ErrorHandlerTest.cs +++ b/test/UnitTest/Components/ErrorHandlerTest.cs @@ -32,31 +32,6 @@ await cut.InvokeAsync(() => dialog.Show(new DialogOption() await cut.InvokeAsync(() => btn.Click()); } - [Fact] - public async Task ShowToast_Ok() - { - var cut = Context.RenderComponent(pb => - { - pb.AddChildContent(); - }); - var errorButton = cut.Find(".btn-error"); - await cut.InvokeAsync(() => errorButton.Click()); - cut.Contains("
test error logger
"); - - // 关闭 Toast - var toast = cut.FindComponent().Instance; - await cut.InvokeAsync(() => toast.Close()); - cut.DoesNotContain("
test error logger
"); - - cut.SetParametersAndRender(pb => - { - pb.Add(a => a.ShowToast, false); - }); - errorButton = cut.Find(".btn-error"); - await cut.InvokeAsync(() => errorButton.Click()); - cut.DoesNotContain("
test error logger
"); - } - private class MockDialogTest : ComponentBase { [Inject] diff --git a/test/UnitTest/Components/LayoutTest.cs b/test/UnitTest/Components/LayoutTest.cs index 6a047aa861a..fa8c89418d0 100644 --- a/test/UnitTest/Components/LayoutTest.cs +++ b/test/UnitTest/Components/LayoutTest.cs @@ -528,13 +528,14 @@ public async Task OnUpdateAsync_Ok() } [Fact] - public void HandlerException_Ok() + public void IHandlerException_Ok() { var cut = Context.RenderComponent(pb => { pb.Add(a => a.EnableErrorLogger, true); pb.AddChildContent(pb => { + // 按钮触发异常 pb.Add(a => a.Main, new RenderFragment(builder => { builder.OpenComponent