序:当去年.NET8 Blazor auto模式原型发布后,好多朋友都以为Blazor auto渲染模式需要写两套代码,或者全部用HttpClient请求,其实都不是正确姿势。本文介绍作者利用源生成器,只需实现服务端的service层,即可让Blazor的后端请求平滑地从server切换到wasm。
原文:
本文简略介绍一下如何使用增量生成器(Incremental Generator)简化BlazorServer兼容Auto模式
比如现在有一个BlazorServer项目的Razor页面
// UserIndex.razor
@code {
[Inject, NotNull] IUserService? Service { get; set; }
}
如果IUserService的实现不支持运行在WebAssembly,比如连接数据库,或者访问服务器文件等等,那么这种情况下,需要Server端提供接口,并且在Client端提供IUserService的接口调用实现
本文的目的就是通过增量生成器,完成Server端接口生成和Client端的接口调用
[WebController(Route = "user", Authorize = true)]
[ApiInvokerGenerate]
public interface IUserService
{
Task<User?> GetUserAsync(string id);
}
nuget地址
dotnet add package AutoWasmApiGenerator --version 0.0.2
在Server端生成Controller
// <auto-generated/>
#pragma warning disable
namespace Project.Constraints.Services
{
[global::Microsoft.AspNetCore.Mvc.ApiController]
[global::Microsoft.AspNetCore.Mvc.Route("api/user")]
[global::Microsoft.AspNetCore.Authorization.Authorize]
[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "1.0.0.0")]
/// <inheritdoc/>
public class UserServiceController : global::Microsoft.AspNetCore.Mvc.ControllerBase
{
private readonly Project.Constraints.Services.IUserService proxyService;
public UserServiceController(Project.Constraints.Services.IUserService service)
{
proxyService = service;
}
[global::Microsoft.AspNetCore.Mvc.HttpPost("GetUser")]
[global::Microsoft.AspNetCore.Authorization.Authorize]
[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "1.0.0.0")]
public System.Threading.Tasks.Task<Project.Constraints.Models.Permissions.User?> GetUserAsync([global::Microsoft.AspNetCore.Mvc.FromBody]string id)
=> proxyService.GetUserAsync(id);
}
}
在Client端生成调用类
// <auto-generated/>
#pragma warning disable
namespace Project.Constraints.Services
{
[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.HttpServiceInvokerGenerator", "1.0.0.0")]
[AutoInjectGenerator.AutoInjectAttribute(Group = "WASM")]
/// <inheritdoc/>
public partial class UserServiceApiInvoker : Project.Constraints.Services.IUserService
{
private readonly global::System.Text.Json.JsonSerializerOptions jsonOptions;
private readonly global::System.Net.Http.IHttpClientFactory clientFactory;
private readonly global::AutoWasmApiGenerator.IHttpClientHeaderHandler headerHandler;
public UserServiceApiInvoker(global::System.Net.Http.IHttpClientFactory factory, global::AutoWasmApiGenerator.IHttpClientHeaderHandler hander)
{
clientFactory = factory;
headerHandler = hander;
jsonOptions = new global::System.Text.Json.JsonSerializerOptions() { PropertyNameCaseInsensitive = true };
}
[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.HttpServiceInvokerGenerator", "1.0.0.0")]
public async System.Threading.Tasks.Task<Project.Constraints.Models.Permissions.User?> GetUserAsync(string id)
{
var url = "api/user/GetUser";
var client = clientFactory.CreateClient("UserService");
var request = new global::System.Net.Http.HttpRequestMessage();
request.Method = global::System.Net.Http.HttpMethod.Post;
headerHandler.SetRequestHeader(request);
var jsonContent = global::System.Text.Json.JsonSerializer.Serialize(id);
request.Content = new StringContent(jsonContent, global::System.Text.Encoding.Default, "application/json");
request.RequestUri = new Uri(url, UriKind.Relative);
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var jsonStream = await response.Content.ReadAsStreamAsync();
return global::System.Text.Json.JsonSerializer.Deserialize<Project.Constraints.Models.Permissions.User?>(jsonStream, jsonOptions);
}
}
}
好的,现在Controller有了,Controller的调用类也有了
项目结构如下,项目的nuget包和引用项目根据Blazor Web App的项目模板进行修改即可
BlazorAdmin
Server端项目BlazorAdmin.Client
Client端项目Shared
双端共用的项目,一般是共用的页面、实体模型、等等
BlazorAdmin Program.cs
......
......
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AutoInject();
builder.Services.AddControllers();
......
......
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies([.. AppConst.Pages]);
app.MapControllers();
......
BlazorAdmin.Client Program.cs
......
......
builder.Services.ConfigureHttpClientDefaults(c =>
{
c.ConfigureHttpClient(h => { h.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress); });
});
builder.Services.AutoInjectWasm();
builder.Services.AddScoped<IUserService, UserServiceApiInvoker>();