【问题标题】:Custom Identity in ASP.Net Core 2.2 Web AppASP.Net Core 2.2 Web 应用程序中的自定义标识
【发布时间】:2019-12-16 14:44:34
【问题描述】:

我正在关注从 Asp Docs 创建自定义 IdentityUser 的演示。我相信的默认 IdentityUser 只记录了用户名、电子邮件和密码。当我注册一个新帐户时,它实际上会记录到 AspNetUser 表中。但是,在我的情况下,我还想记录诸如 FirstName、LastName、DOB 等内容,并且由于默认情况下不记录这些内容,因此我需要创建自定义 IdentityUser。在按照 aspnet 文档实现自定义身份并更新视图的 html 之后,我收到了这个:

https://gyazo.com/5e1494dccc44d3cd03d24bf48816caf9(screenshot)

我不确定它从哪里接收“无效的列名”,我的 VisUser(自定义 IdentityUser)中的列名与我的 sql server VisUser 表中的列名匹配。

数据库上下文:

 public class dbContext : IdentityDbContext<VisUser>
    {
        public dbContext(DbContextOptions<dbContext> options)
            : base(options)
        {
        }
        public DbSet<VisUser> VisUsers { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            // Customize the ASP.NET Identity model and override the defaults if needed.
            // For example, you can rename the ASP.NET Identity table names and more.
            // Add your customizations after calling base.OnModelCreating(builder);
        }
    }

访问用户

public class VisUser : IdentityUser
    {
        [Key]
        public int UserId { get; set; }

        [BindProperty]
        [Required]
        [MinLength(0), MaxLength(50), DataType(DataType.Text), Display(Name = "FirstName")]
        public string FirstName { get; set; }

        [BindProperty]
        [Required]
        [MinLength(0), MaxLength(2), DataType(DataType.Text), Display(Name = "Middle Initial")]
        public string MiddleInitial { get; set; }

        [BindProperty]
        [Required]
        [MinLength(1), MaxLength(50), DataType(DataType.Text), Display(Name = "Last Name")]
        public string LastName { get; set; }

        [BindProperty]
        [Required]
        [MinLength(4), MaxLength(50), DataType(DataType.Text), Display(Name = "Username")]
        public string VisUserName { get; set; }

        [BindProperty]
        [Required]
        [EmailAddress, MaxLength(256), Display(Name = "Email Address")]
        public string VisEmail { get; set; }

        [BindProperty]
        [Required]
        [MinLength(6), MaxLength(50), DataType(DataType.Password), Display(Name = "UserPassword")]
        public string UserPassword { get; set; }

    }

注册页面视图的 HTML

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <h4>Create a new account.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Input.FirstName"></label>
                <input asp-for="Input.FirstName" class="form-control" />
                <span asp-validation-for="Input.FirstName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.MiddleInitial"></label>
                <input asp-for="Input.MiddleInitial" class="form-control" />
                <span asp-validation-for="Input.MiddleInitial" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.LastName"></label>
                <input asp-for="Input.LastName" class="form-control" />
                <span asp-validation-for="Input.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.VisEmail"></label>
                <input asp-for="Input.VisEmail" class="form-control" />
                <span asp-validation-for="Input.VisEmail" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.VisUserName"></label>
                <input asp-for="Input.VisUserName" class="form-control" />
                <span asp-validation-for="Input.VisUserName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.UserPassword"></label>
                <input asp-for="Input.UserPassword" class="form-control" />
                <span asp-validation-for="Input.UserPassword" class="text-danger"></span>
            </div>


            <button type="submit" class="btn btn-primary">Register</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

注册模型:

 [AllowAnonymous]
    public class RegisterModel : PageModel
    {
        private readonly SignInManager<VisUser> _signInManager;
        private readonly UserManager<VisUser> _userManager;
        private readonly ILogger<RegisterModel> _logger;
        private readonly IEmailSender _emailSender;

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

        [BindProperty]
        public InputModel Input { get; set; }

        public string ReturnUrl { get; set; }

        public class InputModel : VisUser
        {
        }

        public void OnGet(string returnUrl = null)
        {
            ReturnUrl = returnUrl;
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");
            if (ModelState.IsValid)
            {
                var user = new VisUser { UserName = Input.VisUserName, Email = Input.VisEmail };
                var result = await _userManager.CreateAsync(user, Input.UserPassword);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { userId = user.Id, code = code },
                        protocol: Request.Scheme);

                    await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                    await _signInManager.SignInAsync(user, isPersistent: false);
                    return LocalRedirect(returnUrl);
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }
    }

当我运行它时,我收到了我的列名无法找到或不匹配的错误。我希望将 VisUser 的信息存储在 sql server 中的 VisUser 表中,并将 AspNetUser 的相关信息也存储在那里,以便我可以使用 SigninManager、UserManager 等操作。希望你能提供帮助提前谢谢你!

【问题讨论】:

    标签: c# sql-server asp.net-core-2.1


    【解决方案1】:

    该错误仅表示您的数据库与您的代码不同步。进行这些更改后,您需要生成并应用迁移,以便数据库为您添加的新道具提供支持列。

    也就是说,这里还有一些其他问题也应该解决。当您从 IdentityUser 继承时,不要定义键。 IdentityUser已经有一个 key 属性。如果您希望该键是 int 而不是字符串的默认值,there's a methodology to do that already

    同样,删除 UserPassword 属性。您最终会将用户的密码(可能以纯文本形式)保存到数据库中,这当然是一个非常糟糕的主意。 IdentityUser 已经内置了密码功能。

    其中一些可能是为了满足InputModel 的需求。但是,您应该绝对不要以这种方式将实体用作模型。您需要一个特定于视图需求的类(即您将帖子绑定到什么以及您显示什么),以及一个特定于数据库需求(即您坚持什么)。不要将两者混为一谈,否则只会有问题。

    最后,不要对用户输入(如名字和姓氏、电子邮件等)施加最大长度等任意限制。这真的没有意义。所有字符串 props 都表示为NVARCHAR,因此它们只消耗它们需要消耗的空间。例如,如果名字有 10 个字符长,它将消耗 20 个字节(unicode),无论您是否施加 50 个限制。如果有些东西实际上有一个逻辑定义的长度,比如社会保险号、邮政编码等,那很好。否则,保持打开状态。

    【讨论】:

    • 谢谢,一切都很好,但是当我删除 UserPassword 属性时,我在注册页面后面的视图和代码中收到错误“UserPassword is missing”,但由于我的 InputModel : VisUser 和我的 InputModel 来自视图要求输入密码(用于注册帐户)如果我从 VisUser 身份中取出密码信息并在 UserManager 中使用它,我应该在哪里放置密码信息?您说它很可能会以纯文本形式存在,但是如果我将其删除,我应该如何使用输入的密码注册用户帐户?
    • 正如我在回答中所说,您需要两个单独的类:用于持久性的实体类和要绑定的视图模型。 UserPassword 可以继续你的视图模型,而不是你的实体(即继承自 IdentityUser 的实体)。
    • 我的 VisUser 实体应该有 0 个引用吗?在我执行“InputModel:VisUser”之前,VisUser 中的属性被引用的次数是剃刀页面中提到的次数,但是当我删除它时,属性有 0 个引用。应用程序如何知道 InputModel 中的信息应该存储在我的 sql server 中的 User 表中的什么位置?
    • 这取决于你。您将输入模型中的值映射到您的实体类。有像 AutoMapper 这样的库可以使这更加自动化,但重点是您应该负责进入数据库的内容,而不是您的用户。
    猜你喜欢
    • 1970-01-01
    • 2019-09-14
    • 2018-09-03
    • 1970-01-01
    • 2019-07-09
    • 2017-12-14
    • 1970-01-01
    • 2018-11-30
    • 1970-01-01
    相关资源
    最近更新 更多