Blazor交互式WebAssembly直接导航页面时触发NullReferenceException
问题描述
开发交互式WebAssembly的Server/Client Blazor应用时,遇到以下问题:
- 从租户列表页面点击链接导航到详情页(
/tenants/{Id})时功能正常; - 直接在浏览器地址栏输入详情页URL时,触发
NullReferenceException,异常来自客户端TenantPage的BuildRenderTree方法,访问targetTenant.Name时因targetTenant为空导致。
原因分析
Blazor WebAssembly组件的渲染流程中,直接访问URL时,组件会先执行初始渲染,此时异步数据加载逻辑(OnInitializedAsync)尚未完成,targetTenant仍为null,渲染时直接访问其属性就会触发空引用异常。此外当前代码还存在两个潜在问题:
TenantClientService未正确处理服务端返回的HTTP错误状态码,导致租户不存在时返回null而非抛出异常;- 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
相关产品推荐
相关产品推荐

