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

如何将ASP.NET Core的Account脚手架迁移至Controller?

解决ASP.NET Core Identity注册页面迁移至MVC视图的模型为空问题

问题根源

你直接复用Razor Page的RegisterModel是错误的,它是为Razor Page设计的PageModel子类,依赖Razor Page的上下文(如PageContext、ModelState)才能正常工作。手动实例化并调用OnGetAsync无法正确初始化这些依赖,导致视图无法识别模型,抛出空模型异常。

正确解决方案

1. 创建独立的MVC注册视图模型

不要复用Razor Page的RegisterModel,创建适配MVC的ViewModel:

using System.ComponentModel.DataAnnotations;

public class RegisterViewModel
{
    [Required]
    [EmailAddress]
    [Display(Name = "邮箱")]
    public string Email { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "密码长度至少为 {2} 个字符,最多为 {1} 个字符。", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "密码")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "确认密码")]
    [Compare("Password", ErrorMessage = "密码和确认密码不匹配。")]
    public string ConfirmPassword { get; set; }
}

2. 改造AccountController的Register动作

直接在控制器中处理注册逻辑,不再依赖Razor Page的Model:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

public class AccountController : Controller
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly SignInManager<IdentityUser> _signInManager;
    private readonly ILogger<AccountController> _logger;
    private readonly IEmailSender _emailSender;

    public AccountController(UserManager<IdentityUser> userManager,
                             SignInManager<IdentityUser> signInManager,
                             ILogger<AccountController> logger,
                             IEmailSender emailSender)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
        _emailSender = emailSender;
    }

    [HttpGet]
    public IActionResult Register()
    {
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
    {
        returnUrl ??= Url.Content("~/");
        if (ModelState.IsValid)
        {
            var user = new IdentityUser { UserName = model.Email, Email = model.Email };
            var result = await _userManager.CreateAsync(user, model.Password);
            
            if (result.Succeeded)
            {
                _logger.LogInformation("用户创建新账号成功");

                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                var callbackUrl = Url.Action(nameof(ConfirmEmail), "Account",
                    new { userId = user.Id, code = code }, HttpContext.Request.Scheme);

                await _emailSender.SendEmailAsync(model.Email, "确认账号",
                    $"请点击<a href='{callbackUrl}'>此链接</a>完成账号确认");

                if (_userManager.Options.SignIn.RequireConfirmedAccount)
                {
                    return RedirectToAction(nameof(RegisterConfirmation), new { email = model.Email });
                }
                else
                {
                    await _signInManager.SignInAsync(user, isPersistent: false);
                    return LocalRedirect(returnUrl);
                }
            }
            
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }

        // 验证失败,返回表单
        return View(model);
    }

    // 可选:添加账号确认动作
    public IActionResult RegisterConfirmation(string email)
    {
        if (email == null)
        {
            return RedirectToAction(nameof(HomeController.Index), "Home");
        }
        return View(new { Email = email });
    }

    // 可选:实现邮箱确认动作
    [HttpGet]
    public async Task<IActionResult> ConfirmEmail(string userId, string code)
    {
        if (userId == null || code == null)
        {
            return RedirectToAction(nameof(HomeController.Index), "Home");
        }
        var user = await _userManager.FindByIdAsync(userId);
        if (user == null)
        {
            return NotFound($"无法找到ID为 {userId} 的用户");
        }
        var result = await _userManager.ConfirmEmailAsync(user, code);
        return View(result.Succeeded ? "ConfirmEmail" : "Error");
    }
}

3. 创建MVC注册视图

在Views/Account/Register.cshtml中使用MVC视图语法,绑定新的ViewModel:

@model RegisterViewModel

@{
    ViewData["Title"] = "注册";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-action="Register">
            <h4>创建新账号</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Email"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ConfirmPassword"></label>
                <input asp-for="ConfirmPassword" class="form-control" />
                <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">注册</button>
        </form>
    </div>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

关键说明

  • Razor Page的PageModel和MVC的ViewModel设计逻辑完全不同,不能跨场景复用
  • 手动实例化PageModel会缺失上下文依赖,导致模型状态异常
  • MVC视图通过asp-for标签助手绑定ViewModel字段,和Razor Page的语法兼容但依赖的模型类型不同

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:54:56