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

ASP.NET Core中两次使用PartialAsync()导致Input为null的问题

问题分析与解决方案

核心问题原因

  1. 模型绑定不匹配:通过Html.PartialAsync传递registerModel给分部视图时,表单生成的字段名是Input.Email、Input.Password这类,但提交后ASP.NET Core会尝试将这些字段绑定到MainLoginModel,而非你构造注入的registerModel实例——因为注入的子PageModel不会被自动填充表单数据。
  2. 分部视图模型上下文错误:分部视图使用子PageModel时未设置正确的模型绑定前缀,导致提交的表单数据无法映射到子模型的Input属性。

解决方案

方案一:重构主模型,直接包含输入模型(推荐)

将注册、登录的输入逻辑整合到MainLoginModel中,避免子PageModel的绑定问题:

后端代码(MainLogin.cshtml.cs)

public class MainLoginModel : PageModel
{
    private readonly IMediator mediator;

    // 直接定义注册、登录的输入模型,并标记为可绑定
    [BindProperty]
    public RegisterInput RegisterInput { get; set; } = new();
    [BindProperty]
    public LoginInput LoginInput { get; set; } = new();

    public bool ShowRegister { get; set; }

    public MainLoginModel(IMediator mediator)
    {
        this.mediator = mediator;
    }

    public async Task<IActionResult> OnGetAsync()
    {
        ShowRegister = (await mediator.Send(new CheckUsers())).UsersDontExist;
        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        ShowRegister = (await mediator.Send(new CheckUsers())).UsersDontExist;
        
        if (ShowRegister)
        {
            if (!ModelState.IsValid) return Page();
            // 执行注册逻辑(把原RegisterModel.OnPostAsync里的代码移到这里,或封装成独立服务)
            await mediator.Send(new RegisterCommand(RegisterInput.Email, RegisterInput.Password));
        }
        else
        {
            if (!ModelState.IsValid) return Page();
            // 执行登录逻辑
            await mediator.Send(new LoginCommand(LoginInput.Email, LoginInput.Password));
        }

        return RedirectToPage("/Index");
    }

    // 定义输入模型
    public class RegisterInput
    {
        public string Email { get; set; }
        public string Password { get; set; }
        public string ConfirmPassword { get; set; }
    }

    public class LoginInput
    {
        public string Email { get; set; }
        public string Password { get; set; }
    }
}

前端代码(MainLogin.cshtml)

修改分部视图的参数,传递对应的输入模型:

@model Path.To.MainLoginModel

@if (Model.ShowRegister)
{
    @await Html.PartialAsync("Path/To/Register.cshtml", Model.RegisterInput);
}else{
    @await Html.PartialAsync("Path/To/Login.cshtml", Model.LoginInput);
}

同时调整分部视图的模型类型(比如Register.cshtml的模型改为RegisterInput),保持表单的asp-for绑定正常即可。


方案二:保留子PageModel,设置模型绑定前缀

如果不想重构原有子PageModel的逻辑,可通过设置视图数据前缀让表单字段与主模型的子属性匹配:

后端代码(MainLogin.cshtml.cs)

public class MainLoginModel : PageModel
{
    private readonly IMediator mediator;
    private readonly IServiceProvider _serviceProvider;

    // 将子模型标记为可绑定属性
    [BindProperty]
    public RegisterModel RegisterModel { get; set; }
    [BindProperty]
    public LoginModel LoginModel { get; set; }

    public bool ShowRegister { get; set; }

    public MainLoginModel(IMediator mediator, IServiceProvider serviceProvider)
    {
        this.mediator = mediator;
        _serviceProvider = serviceProvider;
    }

    public async Task<IActionResult> OnGetAsync()
    {
        // 初始化子模型
        RegisterModel = _serviceProvider.GetRequiredService<RegisterModel>();
        LoginModel = _serviceProvider.GetRequiredService<LoginModel>();
        ShowRegister = (await mediator.Send(new CheckUsers())).UsersDontExist;
        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        ShowRegister = (await mediator.Send(new CheckUsers())).UsersDontExist;
        
        if (ShowRegister)
        {
            if (!ModelState.IsValid) return Page();
            await RegisterModel.OnPostAsync();
        }
        else
        {
            if (!ModelState.IsValid) return Page();
            await LoginModel.OnPostAsync();
        }

        return RedirectToPage("/Index");
    }
}

前端代码(MainLogin.cshtml)

调用分部视图时添加前缀:

@model Path.To.MainLoginModel

@if (Model.ShowRegister)
{
    @await Html.PartialAsync("Path/To/Register.cshtml", Model.RegisterModel, 
        new ViewDataDictionary(ViewData) { Prefix = nameof(Model.RegisterModel) });
}else{
    @await Html.PartialAsync("Path/To/Login.cshtml", Model.LoginModel, 
        new ViewDataDictionary(ViewData) { Prefix = nameof(Model.LoginModel) });
}

这样分部视图生成的表单字段会自动带上RegisterModel.前缀(比如RegisterModel.Input.Email),提交后模型绑定会正确填充主模型的RegisterModel.Input属性。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 06:07:06