我阅读了大量关于在单个应用程序中切换 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
我意识到这是一个“大”问题,但希望这可以将其提炼为关键要素。我怀疑我忽略的架构中缺少一些基本的东西。
答案 0 :(得分:0)
在查看您的存储库后更新
您需要在 Blazor 服务器启动中注册一个 HttpClient
Blazor_WASM_Server.Server 中的 startup.cs 应如下所示:
using Blazor_WASM_Server.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Linq;
using System.Net.Http;
namespace Blazor_WASM_Server.Server
{
public enum HybridType
{
ServerSide,
WebAssembly//,
//HybridManual,
//HybridOnNavigation,
//HybridOnReady
}
public class HybridOptions
{
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; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
if (hybridOptions.HybridType == HybridType.ServerSide)
{
services.AddServerSideBlazor();
// Server Side Blazor doesn't register HttpClient by default
// Thanks to Robin Sue - Suchiman https://github.com/Suchiman/BlazorDualMode
if (!services.Any(x => x.ServiceType == typeof(HttpClient)))
{
// Setup HttpClient for server side in a client side compatible fashion
services.AddScoped<HttpClient>(s =>
{
// Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
var uriHelper = s.GetRequiredService<NavigationManager>();
return new HttpClient
{
BaseAddress = new Uri(uriHelper.BaseUri)
};
});
}
services.AddScoped<WebApiClient>();
}
services.Configure<HybridOptions>(Configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
您可以在 https://github.com/ShaunCurtis/AllinOne 处查看我对合并网站的看法,