【发布时间】:2021-09-22 12:21:05
【问题描述】:
我已经阅读了大量关于在单个应用程序中切换 WASM 和服务器模式的文章。最符合我要求的是:https://itnext.io/blazor-switching-server-and-webassembly-at-runtime-d65c25fd4d8
我正在尝试设置我的项目,以便由于更好的调试等而可以在服务器模式下开发,并使用 WebAssembly 进行部署。为了隔离我一直面临的问题,我基于标准的 WebAssembly(托管)模板创建了这个 github 存储库。 https://github.com/gwruck/Blazor_WASM_Server。有关详细信息,请参阅自述文件。
该解决方案在 WebAssembly 模式下运行良好,但在服务器模式下,我收到错误消息:“InvalidOperationException:无法为类型 'Blazor_WASM_Server.Client.Pages.Index' 的属性 'api' 提供值。没有注册服务'Blazor_WASM_Server.Client.WebApiClient' 类型。
我在客户端应用程序 (WebApiClient) 中创建了一个 Typed Http 客户端并将其注入到索引页面中。我也尝试过注入许多其他服务,但我似乎无法让它们中的任何一个在服务器模式下工作。
根据这篇文章,我认为这种方法可行。 https://www.pragimtech.com/blog/blazor/call-rest-api-from-blazor/
我知道 WASM 和 Server 对 DI 的处理是不同的,但在这种情况下我根本无法让它工作。这是尝试组合两种 Blazor 的基本限制,还是有人能找到解决方法?如果您需要更多信息来描述问题,请告诉我。
以下是代码的关键元素:
Blazor_WASM_Server.Server
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.0-preview.5.21301.17" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-preview.5.21301.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Client\Blazor_WASM_Server.Client.csproj" />
<ProjectReference Include="..\Shared\Blazor_WASM_Server.Shared.csproj" />
</ItemGroup>
</Project>
namespace Blazor_WASM_Server.Server
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public enum HybridType
{
ServerSide,
WebAssembly//,
}
public class HybridOptions
{
//Choose WebAssembly or ServerSide
public HybridType HybridType { get; set; } = HybridType.ServerSide;
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public HybridOptions hybridOptions = new HybridOptions();
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
if (hybridOptions.HybridType == HybridType.ServerSide)
{
//Conditionally add ServerSide
services.AddServerSideBlazor();//new}
}
services.Configure<HybridOptions>(Configuration);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToPage("/_Host");
});
}
}
_Host.cshtml
@page "_Host"
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using Blazor_WASM_Server.Server
@using Microsoft.Extensions.Options
@using Blazor_WASM_Server.Client
@inject IOptions<HybridOptions> HybridOptions
@{
@*Retrieve the running mode*@
var hybridType = HybridOptions?.Value?.HybridType ?? HybridType.WebAssembly;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Sabre PM</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="css/site.css" rel="stylesheet" />
<link href="_content/Syncfusion.Blazor.Themes/bootstrap4.css" rel="stylesheet" />
</head>
<body>
<div id="app">
**@*Conditionally apply Server/Webassembly. This is where the magic happens*@**
@if (hybridType == HybridType.ServerSide)
{
<component type="typeof(App)" render-mode="ServerPrerendered"/>
<persist-component-state/>
<script src="_framework/blazor.server.js"></script>
}
else if (hybridType == HybridType.WebAssembly)
{
<div id="app">Loading...</div>
<script src="_framework/blazor.webassembly.js"></script>
}
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">????</a>
</div>
</body>
</html>
Blazor_WASM_Server.Client
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.0-preview.5.21301.17" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.0-preview.5.21301.17" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-preview.5.21301.5" />
<PackageReference Include="System.Net.Http.Json" Version="6.0.0-preview.5.21301.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\Blazor_WASM_Server.Shared.csproj" />
</ItemGroup>
</Project>
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<Client.App>("#app");
//Added a typed Http client
builder.Services.AddHttpClient<WebApiClient>(client =>
{
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
});
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
}
}
public class WebApiClient
{
public HttpClient Client { get; }
public WebApiClient(HttpClient client)
{
Client = client;
}
}
@* Index.razor *@
@page "/"
@inject WebApiClient api
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
@code{
//Inject the WebApiClient and verify it has been set
protected override void OnInitialized()
{
**//For some reason, whatever I try to inject here will work in WASM Mode, but not in SeverSide mode**
if (api == null) throw new NullReferenceException("WebApiClient not set by Dependency Injection");
base.OnInitialized();
}
}
我知道这经常混合和匹配范例,但如果您将其设置为读取 Web API,则非常相似的代码将在“纯”服务器端应用程序中运行。我只是无法弄清楚为什么它在这个混合版本中不起作用。我怀疑这与项目类型有关(Microsoft.NET.Sdk.BlazorWebAssembly vs Microsoft.NET.Sdk.Web
我意识到这是一个“大”问题,但希望这能将其提炼为关键要素。我怀疑我忽略的架构中缺少一些基本的东西。
【问题讨论】:
-
当服务器模式出现问题时,发布该代码更有意义。 Startup.cs 和 Program.cs 开始。
标签: dependency-injection server blazor webassembly