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

Blazor交互式WebAssembly直接导航页面时触发NullReferenceException

问题描述

开发交互式WebAssembly的Server/Client Blazor应用时,遇到以下问题:

  • 从租户列表页面点击链接导航到详情页(/tenants/{Id})时功能正常;
  • 直接在浏览器地址栏输入详情页URL时,触发NullReferenceException,异常来自客户端TenantPage的BuildRenderTree方法,访问targetTenant.Name时因targetTenant为空导致。
原因分析

Blazor WebAssembly组件的渲染流程中,直接访问URL时,组件会先执行初始渲染,此时异步数据加载逻辑(OnInitializedAsync)尚未完成,targetTenant仍为null,渲染时直接访问其属性就会触发空引用异常。此外当前代码还存在两个潜在问题:

  1. TenantClientService未正确处理服务端返回的HTTP错误状态码,导致租户不存在时返回null而非抛出异常;
  2. TenantPage手动实例化TenantClientService,未利用依赖注入,不符合Blazor最佳实践。
解决方案

1. 组件渲染时添加null检查与加载状态

在TenantPage中添加加载状态标记,或直接对targetTenant进行null判断,避免初始渲染时访问null属性:

修改后的TenantPage代码:

@page "/tenants/{Id}"
@inject TenantClientService tenantService <!-- 改为依赖注入服务 -->

<!-- 根据状态渲染不同内容 -->
@if (IsLoading)
{
    <p>加载中...</p>
}
else if (targetTenant == null)
{
    <p>未找到指定租户</p>
}
else
{
    <h1>@targetTenant.Name</h1>
    <!-- 其他租户详情内容 -->
}

@code {
    [Parameter]
    public string Id { get; set; } = null!;

    private Tenant? targetTenant;
    private bool IsLoading = true;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            var tenantId = Guid.Parse(Id);
            targetTenant = await tenantService.GetTenantAsync(tenantId);
        }
        catch (Exception ex)
        {
            // 处理异常,比如记录日志或显示错误信息
            Console.WriteLine(ex.Message);
        }
        finally
        {
            IsLoading = false;
        }
    }
}

2. 修复TenantClientService的HTTP状态码处理

当服务端返回404或其他错误状态时,主动抛出异常,避免返回null导致组件逻辑混乱:

修改后的TenantClientService代码:

internal async Task<Tenant> GetTenantAsync(Guid id)
{
    Logger.LogInformation("Getting tenant with id {id}", id);
    var response = await HttpClient.GetAsync($"{_endpoint}/{id}");
    
    // 非成功状态直接抛出异常
    response.EnsureSuccessStatusCode();
    
    var tenant = await response.Content.ReadFromJsonAsync<Tenant>();
    if(tenant is null)
    {
        Logger.LogError("Failed to deserialize tenant with id {id}", id);
        throw new InvalidOperationException($"Failed to load tenant with ID {id}");
    }
    return tenant;
}

3. 注册依赖注入服务

在Program.cs中注册TenantClientService,替代手动实例化:

builder.Services.AddScoped<TenantClientService>();

4. 服务端异常处理优化(可选)

将仓储中抛出的ArgumentNullException改为自定义异常,便于客户端精准处理:

public async Task<Tenant> GetTenantByIdAsync(Guid id)
{
    Logger.LogInformation("Getting tenant with id {id}", id);
    Tenant? tenant = await Context.Tenants.FindAsync(id);
    if (tenant is null)
    {
        Logger.LogError("Tenant with id {id} not found", id);
        throw new TenantNotFoundException($"Tenant with ID {id} not found");
    }
    return tenant;
}

// 自定义异常类
public class TenantNotFoundException : Exception
{
    public TenantNotFoundException(string message) : base(message) { }
}

控制器中捕获该异常并返回404:

[HttpGet("{id}")]
public async Task<IActionResult> Get(Guid id)
{
    try
    {
        var tenant = await tenantRepository.GetTenantByIdAsync(id);
        return Ok(tenant);
    }
    catch (TenantNotFoundException)
    {
        return NotFound();
    }
}

内容的提问来源于stack exchange,提问作者Theologos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 14:37:11