You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

渲染时异常显示问题: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>

页面偶现空白的原因

  1. ErrorContent自身渲染抛出二次异常:
    日志输出说明初始异常已被处理,但ErrorContent中的日志逻辑可能触发新异常。比如Logger实例为null时调用Logger.LogError会触发空引用异常;或者LoggerFactory.CreateLogger(category)中category类型无效,导致创建日志实例失败,这些异常会让ErrorContent渲染失败,若没有更上层的ErrorBoundary捕获,整个页面就会空白。

  2. 组件渲染流程的异常穿透:
    初始异常来自DevExpress Grid组件的渲染环节,组件内部的渲染逻辑可能存在异常穿透,导致当前ErrorBoundary无法完全处理,最终页面渲染中断。

  3. 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,确保注入逻辑正确,避免Logger为null:

@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 16:10:42