.NET 8 Blazor WebAssembly应用如何仅客户端取数并保留服务端预渲染
.NET 8 Blazor WebAssembly全局交互模式下预渲染跳过API调用的解决方案
问题背景
使用Visual Studio的.NET 8 Blazor模板创建WebAssembly交互模式、全局交互范围的应用,生成Server和Client双项目。在组件的OnInitializedAsync方法中通过HttpClient调用API加载数据时,触发HttpClient未注册为服务的错误——原因是应用默认会在服务端预渲染页面,但服务端项目未注册HttpClient服务。
尝试过两种方案均不适用:
- 方案1:创建共享接口,Server和Client分别实现对应服务并通过依赖注入切换。但该方案会绕过控制器的授权机制,直接从服务层获取数据,不符合业务需求。
- 方案2:在服务端和客户端注册命名
HttpClient,但直接使用@inject HttpClient HttpClient仍会报错,无法正常工作。
需求明确:无需在服务端获取数据,仅在客户端初始化组件时执行API调用;需保留服务端预渲染的所有UI元素,但跳过服务端的API调用,且不希望使用OnAfterRender方法(执行时机过晚)。
解决方案
步骤1:服务端注册HttpClient避免DI注入失败
在Server项目的Program.cs中添加HttpClient的注册,为服务端预渲染提供占位实现,防止组件注入时出错:
// 服务端预渲染时注册HttpClient,避免DI注入失败 builder.Services.AddScoped<HttpClient>();
步骤2:组件中判断运行环境,仅客户端执行API调用
在.NET 8中可直接使用OperatingSystem.IsBrowser()判断当前是否处于浏览器(客户端)环境,仅满足条件时执行API调用。修改Client项目的Home.razor:
@page "/" @inject HttpClient HttpClient <div>Elements: @forecasts?.Count()</div> Some UI controls here @code { IEnumerable<Weather>? forecasts; protected override async Task OnInitializedAsync() { // 仅在客户端运行时执行API调用 if (OperatingSystem.IsBrowser()) { forecasts = await HttpClient.GetFromJsonAsync<IEnumerable<Weather>>("api/weather/list"); } } }
兼容.NET 7及更早版本的替代方案
如果需要兼容更早版本的.NET,可通过注入IJSInProcessRuntime判断运行环境:
@page "/" @inject HttpClient HttpClient @inject IJSInProcessRuntime JS <div>Elements: @forecasts?.Count()</div> Some UI controls here @code { IEnumerable<Weather>? forecasts; protected override async Task OnInitializedAsync() { try { // 尝试调用JS运行时,服务端预渲染时会抛出异常 _ = JS.Invoke<string>("toString"); // 成功则说明在客户端,执行API调用 forecasts = await HttpClient.GetFromJsonAsync<IEnumerable<Weather>>("api/weather/list"); } catch { // 服务端预渲染时跳过API调用 } } }
方案优势
- 保留服务端预渲染的UI结构,用户可快速看到页面框架
- API调用仅在客户端执行,完全遵循控制器的授权逻辑
- 使用
OnInitializedAsync执行,满足初始化时机的需求,无需延后到OnAfterRender
内容的提问来源于stack exchange,提问作者Elshad Shabanov
相关产品推荐
相关产品推荐

