源生成器简化 Blazor WebApp 模式开发

序:当去年.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的项目模板进行修改即可

  • 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值