Blazor Server根据ID获取用户时触发System.NullReferenceException问题
Blazor Server 获取用户时触发 NullReferenceException 问题
问题详情
开发首个 Blazor Server 应用,采用 Blazor Server 前端 + .NET 8 API 后端架构。根据 ID 获取指定用户时触发 System.NullReferenceException,硬编码 ID 测试仍报错,但已验证 API 端点功能正常。
错误堆栈信息:
System.NullReferenceException HResult=0x80004003 Message=Object reference not set to an instance of an object. Source=CalibrationReportingToolFrontend StackTrace: at CalibrationReportingToolFrontend.Components.Pages.OtherPages.UserAdd.BuildRenderTree(RenderTreeBuilder __builder)
相关代码
Blazor 页面代码
@page "/User" @page "/User/{Id:int}" @rendermode InteractiveServer @using System.Net.Http @using Models @using System.Text.Json; @using System.Text @using System.Security.Cryptography; @inject HttpClient HttpClient @inject NavigationManager NavManager @if (Id == null) { <h3>User Add</h3> } else { <h3>User Edit</h3> } <form method="post" @onsubmit="AddUser" @formname="UserAdd" class="mt-4"> <AntiforgeryToken /> <div class="mb-3"> <label for="name" class="form-label">Name:</label> <InputText type="text" @bind-Value="user!.Name" class="form-control" required /> </div> <div class="mb-3"> <label for="Company" class="form-label">Company:</label> <InputText type="text" @bind-Value="user!.Company" class="form-control" required /> </div> <div class="mb-3"> <label for="Department" class="form-label">Department:</label> <InputText type="text" @bind-Value="user!.Department" class="form-control" required /> </div> <div class="mb-3"> <label for="email" class="form-label">Email:</label> <InputText type="email" @bind-Value="user!.Email" class="form-control" required /> </div> <div class="mb-3"> <label for="Password" class="form-label">Password:</label> <InputText type="password" @bind-Value="user!.Password" class="form-control" required /> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> @code { [Parameter] public int? Id { get; set; } [SupplyParameterFromForm] public User? user { get; set; } protected override async Task OnInitializedAsync() { if (Id != null) { try{ var response = await HttpClient.GetAsync($"https://localhost:7183/api/User/{Id}"); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); user = JsonSerializer.Deserialize<User>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } catch (Exception ex) { // Handle error Console.WriteLine("Error getting user: " + ex.Message); } } else { user ??= new(); } } void AddUser() { try { byte[] salt = GenerateSalt(); user.Password = HashPassword(user.Password, salt); var client = new HttpClient(); client.BaseAddress = new Uri("https://localhost:7183/api/User"); var json = JsonSerializer.Serialize(user); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = client.PostAsync("User", content).Result; NavManager.NavigateTo("/UserOverview"); } catch (Exception ex) { // Handle error Console.WriteLine("Error adding user: " + ex.Message); } } private string HashPassword(string password, byte[] salt) { using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000)) { byte[] hash = pbkdf2.GetBytes(32); // 32 bytes for SHA256 byte[] hashBytes = new byte[36]; // 32 bytes for hash + 4 bytes for salt Array.Copy(salt, 0, hashBytes, 0, 4); Array.Copy(hash, 0, hashBytes, 4, 32); return Convert.ToBase64String(hashBytes); } } private byte[] GenerateSalt() { byte[] salt = new byte[4]; // 4 bytes for salt using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider()) { rngCsp.GetBytes(salt); } return salt; } }
API 接口代码
group.MapGet("/{id}", async Task<Results<Ok<User>, NotFound>> (int id, DataContext db) => { return await db.User.AsNoTracking() .FirstOrDefaultAsync(model => model.Id == id) is User model ? TypedResults.Ok(model) : TypedResults.NotFound(); }) .WithName("GetUserById") .WithOpenApi();
原因分析
- 异步渲染时机问题:Blazor 组件会先完成首次渲染,再执行
OnInitializedAsync中的异步 API 请求。当Id不为 null 时,首次渲染时user尚未被赋值,UI 中的user!.Name等表达式会因user为 null 触发异常。 - 错误处理缺失:API 请求失败时,catch 块仅打印错误,未给
user赋默认值,导致后续渲染仍使用 null 对象。 SupplyParameterFromForm干扰:该特性会尝试从表单绑定user,但编辑场景下是从 API 获取数据,此特性可能破坏user的初始化逻辑。
解决方案
1. 确保user始终非空
在OnInitializedAsync开头先初始化user,并在 API 请求失败时保证user有默认值:
protected override async Task OnInitializedAsync() { // 提前初始化,避免首次渲染null user ??= new User(); if (Id != null) { try{ var response = await HttpClient.GetAsync($"https://localhost:7183/api/User/{Id}"); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); var fetchedUser = JsonSerializer.Deserialize<User>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); // 反序列化成功再赋值 if(fetchedUser != null) { user = fetchedUser; } } catch (Exception ex) { Console.WriteLine("Error getting user: " + ex.Message); // 出错时重置为默认对象 user = new User(); } } }
2. 移除SupplyParameterFromForm特性
编辑场景不需要从表单绑定user,移除该特性消除干扰:
// 移除[SupplyParameterFromForm] public User? user { get; set; }
3. 添加加载状态(可选)
在异步加载期间显示加载提示,避免用户看到异常UI:
@if (user == null) { <p>Loading user data...</p> } else { <!-- 原表单内容 --> }
4. 修复AddUser方法的 HttpClient 使用
避免新建 HttpClient,使用注入的实例并改为异步方法:
async Task AddUser() { try { byte[] salt = GenerateSalt(); user.Password = HashPassword(user.Password, salt); var json = JsonSerializer.Serialize(user); var content = new StringContent(json, Encoding.UTF8, "application/json"); // 使用注入的HttpClient var response = await HttpClient.PostAsync("https://localhost:7183/api/User", content); response.EnsureSuccessStatusCode(); NavManager.NavigateTo("/UserOverview"); } catch (Exception ex) { Console.WriteLine("Error adding user: " + ex.Message); } }
内容的提问来源于stack exchange,提问作者JuleanCode
相关产品推荐
相关产品推荐

