You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Blazor Web App注册页部分代码以WASM模式运行

实现方案:Blazor Web App 混合渲染的注册页面

针对你需要服务器端渲染/实时验证 + 客户端提交回调的需求,以下是两种可行的落地方案:


这种方案让页面初始渲染和字段验证完全在服务器端执行,仅用客户端 JS 完成 Cookie 设置逻辑,兼顾性能和需求。

1. 切换为服务器端渲染

移除组件顶部的 @rendermode InteractiveWebAssembly,让注册页面默认使用 Blazor Web App 的静态服务器渲染(SSR),确保页面初始渲染在服务器完成。

2. 实现服务器端实时验证

替换客户端的本地验证逻辑,改为字段变化时触发服务器端验证:

protected override void OnInitialized()
{
    RegisterModel ??= new();
    editContext = new(RegisterModel);
    validationMessageStore = new(editContext);
    
    // 字段变化时触发服务器端验证
    editContext.OnFieldChanged += async (_, args) =>
    {
        validationMessageStore.Clear(args.FieldIdentifier);
        
        // 服务器端执行DataAnnotations验证(或调用专门的验证API)
        var validationResult = await ValidateFieldOnServer(args.FieldIdentifier.FieldName);
        
        if (!validationResult.IsValid)
        {
            validationMessageStore.Add(args.FieldIdentifier, validationResult.Errors.Select(e => e.ErrorMessage));
        }
        
        editContext.NotifyValidationStateChanged();
    };
}

// 服务器端字段验证方法
private async Task<ValidationResult> ValidateFieldOnServer(string fieldName)
{
    var validationContext = new ValidationContext(RegisterModel) { MemberName = fieldName };
    var results = new List<ValidationResult>();
    Validator.TryValidateProperty(
        RegisterModel.GetType().GetProperty(fieldName)!.GetValue(RegisterModel),
        validationContext,
        results
    );
    return results.Any() ? results.First() : ValidationResult.Success!;
}

注入 IJSRuntime,在提交成功后调用客户端 JS 设置认证 Cookie:

[Inject]
private IJSRuntime JSRuntime { get; set; } = null!;

private async Task HandleOnValidSubmit()
{
    isProcessing = true;
    validationMessageStore.Clear();

    HttpResponseMessage res = await api.HttpClient.PostAsJsonAsync("auth/register", RegisterModel);

    if (res.IsSuccessStatusCode)
    {
        // 从API响应获取认证信息(如Token)
        var authToken = await res.Content.ReadAsStringAsync();
        // 调用客户端JS设置Cookie
        await JSRuntime.InvokeVoidAsync("setAuthCookie", authToken, RegisterModel.KeepLogin);
        
        navigation.NavigateTo("/");
    }
    else if (res.StatusCode == System.Net.HttpStatusCode.BadRequest)
    {
        // 原有错误处理逻辑不变
        HttpValidationProblemDetails problemDetails = (await res.Content.ReadFromJsonAsync<HttpValidationProblemDetails>())!;
        if (problemDetails.Errors != null)
        {
            foreach (var message in problemDetails.Errors)
            {
                validationMessageStore.Add(new(RegisterModel, message.Key), message.Value);
            }
            editContext.NotifyValidationStateChanged();
        }
    }

    isProcessing = false;
}

在 wwwroot/js/form.js 中添加 Cookie 设置函数:

function setAuthCookie(token, keepLogin) {
    const expires = keepLogin ? new Date(Date.now() + 30*24*60*60*1000) : null;
    document.cookie = `AuthToken=${token}; expires=${expires ? expires.toUTCString() : ''}; path=/; SameSite=Lax; Secure`;
}

方案二:拆分组件 - SSR 表单 + 客户端提交按钮

将提交逻辑封装为独立的客户端交互组件,主表单保持 SSR,兼顾服务器端验证和客户端提交需求。

1. 创建客户端提交按钮组件 (RegisterSubmitButton.razor)

@rendermode InteractiveWebAssembly
@inject ApiHttpClient api
@inject NavigationManager navigation
@inject IJSRuntime JSRuntime

<button type="submit" class="cta-btn primary-cta-btn" disabled="@isProcessing" @onclick="HandleSubmit">
    @if (!isProcessing)
    {
        @RegisterText
    }
    else
    {
        <span class="loader"></span>
    }
</button>

@code {
    [Parameter] public RegisterModel RegisterModel { get; set; } = null!;
    [Parameter] public string RegisterText { get; set; } = "Register";
    private bool isProcessing = false;

    private async Task HandleSubmit()
    {
        isProcessing = true;

        HttpResponseMessage res = await api.HttpClient.PostAsJsonAsync("auth/register", RegisterModel);

        if (res.IsSuccessStatusCode)
        {
            var authToken = await res.Content.ReadAsStringAsync();
            await JSRuntime.InvokeVoidAsync("setAuthCookie", authToken, RegisterModel.KeepLogin);
            navigation.NavigateTo("/");
        }
        
        isProcessing = false;
    }
}

2. 修改主注册组件

移除原有的提交按钮,引入客户端提交组件,同时保留服务器端实时验证逻辑:

@page "/auth/register"
@using ClassLibrary.Models.Auth
@inject IStringLocalizer<Register> localizer
@inject ApiHttpClient api

<HeadContent>
    <link rel="stylesheet" href="/css/auth/login.css" />
    <link rel="stylesheet" href="/css/components/loader.css" />
</HeadContent>
<div class="content-container">
    <EditForm FormName="RegisterForm" EditContext="editContext" class="data-form">
        <h1>@localizer["PageTitle"]</h1>
        <fieldset disabled="@isProcessing">
            <!-- 原有表单字段部分保持不变 -->
        </fieldset>
        <fieldset class="form-checkbox-area" disabled="@isProcessing">
            <!-- 原有复选框部分保持不变 -->
        </fieldset>
        <!-- 引入客户端提交按钮 -->
        <RegisterSubmitButton RegisterModel="RegisterModel" RegisterText="@localizer["Register"]" />
        <p class="form-hint">@((MarkupString)localizer["FormHint"].Value)</p>
    </EditForm>
</div>
<script src="/js/form.js"></script>

@code {
    private RegisterModel RegisterModel { get; set; } = null!;
    private EditContext editContext = null!;
    private ValidationMessageStore validationMessageStore = null!;
    private bool isProcessing = false;

    [Inject]
    private NavigationManager navigation { get; set; } = null!;

    protected override void OnInitialized()
    {
        RegisterModel ??= new();
        editContext = new(RegisterModel);
        validationMessageStore = new(editContext);
        // 服务器端实时验证逻辑同方案一
        editContext.OnFieldChanged += async (_, args) =>
        {
            validationMessageStore.Clear(args.FieldIdentifier);
            var validationResult = await ValidateFieldOnServer(args.FieldIdentifier.FieldName);
            if (!validationResult.IsValid)
            {
                validationMessageStore.Add(args.FieldIdentifier, validationResult.Errors.Select(e => e.ErrorMessage));
            }
            editContext.NotifyValidationStateChanged();
        };
    }

    private async Task<ValidationResult> ValidateFieldOnServer(string fieldName)
    {
        var validationContext = new ValidationContext(RegisterModel) { MemberName = fieldName };
        var results = new List<ValidationResult>();
        Validator.TryValidateProperty(
            RegisterModel.GetType().GetProperty(fieldName)!.GetValue(RegisterModel),
            validationContext,
            results
        );
        return results.Any() ? results.First() : ValidationResult.Success!;
    }
}

核心注意事项

  1. 服务器端验证:避免客户端和服务器端验证规则不一致,所有实时验证逻辑必须在服务器端执行。
  2. Cookie 操作限制:服务器端无法直接修改浏览器 Cookie,必须通过客户端 JS 或 API 响应头的 Set-Cookie 字段完成。
  3. 渲染模式选择:如果需要实时交互(如字段输入时的即时反馈),可以改用 @rendermode InteractiveServer,配合 JS 互操作实现客户端 Cookie 设置,兼顾服务器端验证和客户端提交需求。

内容的提问来源于stack exchange,提问作者iKingNinja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 02:38:09