From 85ca18579a932e866f476d06e6aa8e0007c320c1 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 29 May 2025 17:58:23 +0800 Subject: [PATCH 1/8] fix(BootstrapBlazorErrorBoundary): improve BuildRenderTree logic to render error or fallback content (#6113) - Only renders ChildContent when there is no exception. - Renders custom ErrorContent if provided, otherwise renders default error content. --- .../BootstrapBlazorErrorBoundary.cs | 23 ++++--- .../BootstrapBlazorErrorBoundaryTest.cs | 60 +++++++++++++++++++ 2 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 test/UnitTest/Components/BootstrapBlazorErrorBoundaryTest.cs diff --git a/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs b/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs index f84021837c2..8a47ee218d0 100644 --- a/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs +++ b/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs @@ -12,7 +12,7 @@ namespace BootstrapBlazor.Components; /// /// 内部使用 自定义异常组件 /// -class BootstrapBlazorErrorBoundary : ErrorBoundaryBase +public class BootstrapBlazorErrorBoundary : ErrorBoundaryBase { [Inject] [NotNull] @@ -68,19 +68,18 @@ protected override async Task OnErrorAsync(Exception exception) /// protected override void BuildRenderTree(RenderTreeBuilder builder) { -#if DEBUG - // DEBUG 模式下显示异常堆栈信息到 UI 页面方便开发人员调试 - if (OnErrorHandleAsync == null) + if (CurrentException is null) { - var ex = CurrentException ?? _exception; - if (ex != null) - { - _exception = null; - builder.AddContent(0, ExceptionContent(ex)); - } + builder.AddContent(0, ChildContent); + } + else if (ErrorContent is not null) + { + builder.AddContent(1, ErrorContent(CurrentException)); + } + else + { + builder.AddContent(2, ExceptionContent(CurrentException)); } -#endif - builder.AddContent(1, ChildContent); } private Exception? _exception = null; diff --git a/test/UnitTest/Components/BootstrapBlazorErrorBoundaryTest.cs b/test/UnitTest/Components/BootstrapBlazorErrorBoundaryTest.cs new file mode 100644 index 00000000000..4c8d3f9180f --- /dev/null +++ b/test/UnitTest/Components/BootstrapBlazorErrorBoundaryTest.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +namespace UnitTest.Components; + +public class BootstrapBlazorErrorBoundaryTest : BootstrapBlazorTestBase +{ + // 1. Test rendering of child content when there is no exception + [Fact] + public void ShouldRenderChildContent_WhenNoException() + { + var cut = Context.RenderComponent(parameters => parameters + .AddChildContent("

Normal Content

") + ); + cut.MarkupMatches("

Normal Content

"); + } + + // 2. Test rendering custom error content when an exception is thrown and ErrorContent is set + [Fact] + public void ShouldRenderCustomErrorContent_WhenExceptionOccurs() + { + var errorContentRendered = false; + + var cut = Context.RenderComponent(parameters => parameters + .Add(p => p.ErrorContent, ex => builder => + { + errorContentRendered = true; + builder.OpenElement(0, "div"); + builder.AddAttribute(1, "class", "custom-error"); + builder.AddContent(2, $"Custom error caught: {ex.Message}"); + builder.CloseElement(); + }) + .AddChildContent() + ); + + cut.MarkupMatches("""
Custom error caught: Boom!
"""); + Assert.True(errorContentRendered); + } + + // 3. Test rendering default error content when an exception is thrown and ErrorContent is null + [Fact] + public void ShouldRenderDefaultExceptionContent_WhenExceptionOccurs_AndErrorContentIsNull() + { + var cut = Context.RenderComponent(parameters => parameters + .AddChildContent() + ); + Assert.Contains("Boom!", cut.Markup); + } + + private class ExceptionThrowingComponent : ComponentBase + { + protected override void OnInitialized() + { + throw new InvalidOperationException("Boom!"); + } + } +} + From 8d3e9e5ebd398f471b2c1c077c83105b7543c19d Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 30 May 2025 12:30:09 +0800 Subject: [PATCH 2/8] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BootstrapBlazorErrorBoundary.cs | 2 +- .../BootstrapBlazorErrorBoundaryTest.cs | 60 ------------------- 2 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 test/UnitTest/Components/BootstrapBlazorErrorBoundaryTest.cs diff --git a/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs b/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs index 47dae0f62f4..f9b13ed90b5 100644 --- a/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs +++ b/src/BootstrapBlazor/Components/ErrorLogger/BootstrapBlazorErrorBoundary.cs @@ -12,7 +12,7 @@ namespace BootstrapBlazor.Components; /// /// 内部使用 自定义异常组件 /// -public class BootstrapBlazorErrorBoundary : ErrorBoundaryBase +class BootstrapBlazorErrorBoundary : ErrorBoundaryBase { [Inject] [NotNull] diff --git a/test/UnitTest/Components/BootstrapBlazorErrorBoundaryTest.cs b/test/UnitTest/Components/BootstrapBlazorErrorBoundaryTest.cs deleted file mode 100644 index 4c8d3f9180f..00000000000 --- a/test/UnitTest/Components/BootstrapBlazorErrorBoundaryTest.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License -// See the LICENSE file in the project root for more information. -// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone - -namespace UnitTest.Components; - -public class BootstrapBlazorErrorBoundaryTest : BootstrapBlazorTestBase -{ - // 1. Test rendering of child content when there is no exception - [Fact] - public void ShouldRenderChildContent_WhenNoException() - { - var cut = Context.RenderComponent(parameters => parameters - .AddChildContent("

Normal Content

") - ); - cut.MarkupMatches("

Normal Content

"); - } - - // 2. Test rendering custom error content when an exception is thrown and ErrorContent is set - [Fact] - public void ShouldRenderCustomErrorContent_WhenExceptionOccurs() - { - var errorContentRendered = false; - - var cut = Context.RenderComponent(parameters => parameters - .Add(p => p.ErrorContent, ex => builder => - { - errorContentRendered = true; - builder.OpenElement(0, "div"); - builder.AddAttribute(1, "class", "custom-error"); - builder.AddContent(2, $"Custom error caught: {ex.Message}"); - builder.CloseElement(); - }) - .AddChildContent() - ); - - cut.MarkupMatches("""
Custom error caught: Boom!
"""); - Assert.True(errorContentRendered); - } - - // 3. Test rendering default error content when an exception is thrown and ErrorContent is null - [Fact] - public void ShouldRenderDefaultExceptionContent_WhenExceptionOccurs_AndErrorContentIsNull() - { - var cut = Context.RenderComponent(parameters => parameters - .AddChildContent() - ); - Assert.Contains("Boom!", cut.Markup); - } - - private class ExceptionThrowingComponent : ComponentBase - { - protected override void OnInitialized() - { - throw new InvalidOperationException("Boom!"); - } - } -} - From 77a559091facdd4f17318f1f27d7f8c9edfa4e09 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 30 May 2025 16:44:01 +0800 Subject: [PATCH 3/8] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Layout/MainLayout.razor | 4 ++- .../Components/Pages/ErrorPage.razor | 3 +++ .../Components/Pages/ErrorPage.razor.cs | 26 +++++++++++++++++++ .../Components/Samples/GlobalException.razor | 4 +++ .../Samples/GlobalException.razor.cs | 11 +++++++- src/BootstrapBlazor.Server/Locales/en-US.json | 4 ++- src/BootstrapBlazor.Server/Locales/zh-CN.json | 4 ++- 7 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 src/BootstrapBlazor.Server/Components/Pages/ErrorPage.razor create mode 100644 src/BootstrapBlazor.Server/Components/Pages/ErrorPage.razor.cs diff --git a/src/BootstrapBlazor.Server/Components/Layout/MainLayout.razor b/src/BootstrapBlazor.Server/Components/Layout/MainLayout.razor index 3dfc8f120db..ed81f3da6c2 100644 --- a/src/BootstrapBlazor.Server/Components/Layout/MainLayout.razor +++ b/src/BootstrapBlazor.Server/Components/Layout/MainLayout.razor @@ -15,7 +15,9 @@
- @Body + + @Body +
diff --git a/src/BootstrapBlazor.Server/Components/Pages/ErrorPage.razor b/src/BootstrapBlazor.Server/Components/Pages/ErrorPage.razor new file mode 100644 index 00000000000..a5e396cf306 --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Pages/ErrorPage.razor @@ -0,0 +1,3 @@ +@page "/error-page" + +

ErrorPage

diff --git a/src/BootstrapBlazor.Server/Components/Pages/ErrorPage.razor.cs b/src/BootstrapBlazor.Server/Components/Pages/ErrorPage.razor.cs new file mode 100644 index 00000000000..693885cfea0 --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Pages/ErrorPage.razor.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +namespace BootstrapBlazor.Server.Components.Pages; + +/// +/// ErrorPage 组件用于测试全局异常处理功能 +/// +public partial class ErrorPage +{ + /// + /// + /// + protected override void OnInitialized() + { + base.OnInitialized(); + + var a = 1; + var b = 0; + + // 这里会抛出异常 + var c = 1 / b; + } +} diff --git a/src/BootstrapBlazor.Server/Components/Samples/GlobalException.razor b/src/BootstrapBlazor.Server/Components/Samples/GlobalException.razor index 1944521cfbd..6bdb3ac42b5 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/GlobalException.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/GlobalException.razor @@ -72,4 +72,8 @@