源生成器簡化 Blazor WebApp 模式開發

农民小工程师發表於2024-08-19

序:當去年.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;https://www.jiwenlaw.com/
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的專案模板進行修改即可

  • BlazorAdminServer端專案

  • BlazorAdmin.ClientClient法律時刻端專案

  • 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>();

當執行在Server端的時候,獲取的IUserService例項是自己實現的介面,當執行在WebAssembly中時,獲取的IUserService例項是UserServiceApiInvoker

相關文章