渲染时异常显示问题:ErrorBoundary偶发页面空白
Blazor ErrorBoundary 偶现页面空白问题分析
出现的异常信息
Error 12:21:42 [] LouisHowe.web.Shared.MainLayout - Error: 781a8157-884a-4926-a9f5-3497950411a5 ArgumentNullException: Value cannot be null. (Parameter 'stringToEscape') at System.ArgumentNullException.Throw(String paramName) at System.ArgumentNullException.ThrowIfNull(Object argument, String paramName) at System.UriHelper.EscapeString(String stringToEscape, Boolean checkExistingEscaped, SearchValues`1 noEscape) at CommonUtilities.Utilities.UriUtils.<>c.<CreateUrl>b__0_0(ValueTuple`2 p) in C:\Git\LouisHowe\CommonUtilities\Utilities\UriUtils.cs:line 23 at System.Linq.Enumerable.SelectArrayIterator`2.MoveNext() at System.String.Join(String separator, IEnumerable`1 values) at CommonUtilities.Utilities.UriUtils.CreateUrl(String path, ValueTuple`2[] parameters) in C:\Git\LouisHowe\CommonUtilities\Utilities\UriUtils.cs:line 20 at LouisHowe.web.Pages.EventJob.EventGridEdit.<>c__DisplayClass43_0.<BuildRenderTree>b__10(RenderTreeBuilder __builder3) in C:\Git\LouisHowe\LouisHowe.web\Pages\EventJob\EventGridEdit.razor:line 42 at Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddContent(Int32 sequence, RenderFragment fragment) at DevExpress.Blazor.Internal.GridCommandCell.BuildRenderTree(RenderTreeBuilder __builder) at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
当前MainLayout中ErrorBoundary的核心实现
<ErrorContent Context="error"> @{ var guid = Guid.NewGuid().ToString(); var useFallback = true; if (error.Data.Contains("Category")) { var data = error.Data["Category"]; if (data is Type category) { var logger = LoggerFactory.CreateLogger(category); logger.LogError(error, "Error: {guid}", guid); useFallback = false; } } if (useFallback) { Logger.LogError(error, "Error: {guid}", guid); } } <h1>Unexpected Error (sorry)</h1> <p> The program has encountered an unexpected error (yes there are expected errors). This error has been logged and we will look at it. </p> <p>We suggest you <a href="/identity/account/LogOut">logout</a> and then log back in if you wish to try again.</p> <p>If you want to contact us, please include the following error id: @guid when you do.</p> </ErrorContent>
页面偶现空白的原因
ErrorContent自身渲染抛出二次异常:
日志输出说明初始异常已被处理,但ErrorContent中的日志逻辑可能触发新异常。比如Logger实例为null时调用Logger.LogError会触发空引用异常;或者LoggerFactory.CreateLogger(category)中category类型无效,导致创建日志实例失败,这些异常会让ErrorContent渲染失败,若没有更上层的ErrorBoundary捕获,整个页面就会空白。组件渲染流程的异常穿透:
初始异常来自DevExpress Grid组件的渲染环节,组件内部的渲染逻辑可能存在异常穿透,导致当前ErrorBoundary无法完全处理,最终页面渲染中断。ErrorData类型转换分支未处理:
如果error.Data["Category"]存在但不是Type类型,会进入fallback分支,若此时Logger为null,直接调用Logger.LogError会抛出异常,导致ErrorContent无法渲染。
解决建议
1. 给ErrorContent添加异常防护
在日志逻辑外层增加try-catch,避免二次异常导致页面空白,同时对Logger做空值检查:
<ErrorContent Context="error"> @{ var guid = Guid.NewGuid().ToString(); var useFallback = true; try { if (error.Data.Contains("Category")) { var data = error.Data["Category"]; if (data is Type category) { var logger = LoggerFactory.CreateLogger(category); logger?.LogError(error, "Error: {guid}", guid); useFallback = false; } } if (useFallback) { Logger?.LogError(error, "Error: {guid}", guid); } } catch (Exception ex) { Logger?.LogError(ex, "Failed to log original error (Error ID: {guid})", guid); } } <h1>意外错误(抱歉)</h1> <p> 程序遇到了意外错误(我们也会处理预期内的错误)。该错误已被记录,我们会尽快排查。 </p> <p>建议您<a href="/identity/account/LogOut">退出登录</a>后重新登录,再尝试操作。</p> <p>如果您需要联系我们,请提供以下错误ID:@guid</p> </ErrorContent>
2. 修复UriUtils.CreateUrl的空值问题
从根源上避免初始异常发生,在处理参数时过滤空值:
// UriUtils.cs 中修改CreateUrl方法 public static string CreateUrl(string path, (string Key, string Value)[] parameters) { if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); if (parameters == null || parameters.Length == 0) return path; var queryParts = parameters .Where(p => !string.IsNullOrEmpty(p.Key) && p.Value != null) .Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}"); var queryString = string.Join("&", queryParts); return string.IsNullOrEmpty(queryString) ? path : $"{path}?{queryString}"; }
3. 确保Logger实例注入正常
在MainLayout中通过依赖注入获取ILogger
@inject ILogger<MainLayout> Logger @inject ILoggerFactory LoggerFactory
4. 添加顶层ErrorBoundary
在App.razor中添加全局ErrorBoundary,作为最后一层防护:
<ErrorBoundary> <ChildContent> <Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p role="alert">Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router> </ChildContent> <ErrorContent Context="error"> <h1>全局错误</h1> <p>系统发生未处理的错误,请稍后重试。</p> </ErrorContent> </ErrorBoundary>
内容的提问来源于stack exchange,提问作者David Thielen
相关产品推荐
相关产品推荐

