首页 > 解决方案 > ASP .NET Core Identity - 如何更新用户的单个详细信息?

问题描述

我有一个关于在我的 ASP.NET 身份应用程序中更新指定用户数据的问题。我有一个模型,它定义了创建、删除和更新用户的属性和一个控制器。在控制器中,我有一个public async Task<IActionResult> EditRole(string id)编辑选定用户详细信息的方法。数据仅在以下情况下更新:

- 更新所有数据

- 仅更新电子邮件

- 使用电子邮件更新一个至少一个值

CustomUserValidator当我尝试更新电子邮件以外的其他内容时,会出现来自负责在创建帐户时验证用户详细信息的类的异常。异常消息System.NullReferenceException: „Object reference not set to an instance of an object.” Microsoft.AspNetCore.Identity.IdentityUser<TKey>.Email.get returned null. 如下是需要查看的详细信息:

模型 UserModel.cs:

using UserApp.Models;
using System.ComponentModel.DataAnnotations;
namespace UserApp
{
public class EditUserModel
    {
        
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
     
    }
}

控制器 AdminController.cs :

using UserApp.Database;
using UserApp.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace UserApp.Controllers
{
[Authorize(Roles = "Administrator")]
    public class AdminController : Controller
    {
        private readonly UserManager<UserEntity> _userManager;
        private readonly RoleManager<IdentityRole> _roleManager;

        public AdminController(UserManager<UserEntity> userManager, RoleManager<IdentityRole> roleManager)
        {
            this._userManager = userManager;
            this._roleManager = roleManager;
        }
//Views
 public IActionResult EditUser() => View();
 [HttpPost]
        public async Task<IActionResult> EditUser(EditUserModel editUserModel,string id)
        {
            
                var user = await _userManager.FindByIdAsync(id);

            if (user != null)
            {
                user.FirstName = editUserModel.FirstName;
                user.LastName = editUserModel.LastName;
                user.Email = editUserModel.Email;
                var result = await _userManager.UpdateAsync(user);


                if (result.Succeeded)
                {
                    return RedirectToAction("Account");
                }
                else
                {
                    AddErrorsFromResult(result);
                }
            }
            else
            {
                ModelState.AddModelError("", "User not existing");
            }
            return View("Account", _userManager.Users);
       
        }
}

查看 EditUser.cshtml :

@model EditUserModel
@{
    Layout = "_Layout";
}
<h1>User update</h1>

<div asp-validation-summary="All" class="text-danger"></div>
<div class="row-edit">
    <form asp-action="EditUser" method="post">
        <p>First Name</p>
        <input class="register form-control" asp-for="FirstName" />
        <p>Last Name</p>
        <input class="register form-control" asp-for="LastName" />
        <p>Email</p>
        <input class="register form-control" asp-for="Email" />

        <br />
        <input type="submit" value="Save" class="btn btn-primary" />
        @* @foreach (var role in Model.RoleModel.Role)
            {
                <table>
                <tr>
                    <td>@role.Name</td>
                </tr>
                </table>
            }*@
    </form>
    </div>

CustomUserValidator.cs

using UserApp.Database;
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace UserApp.Infrastructure
{
    public class CustomUserValidator : UserValidator<UserEntity>
    {
        public override async Task<IdentityResult> ValidateAsync(UserManager<UserEntity> manager, UserEntity user)
        {
            IdentityResult result = await base.ValidateAsync(manager, user);

            List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();

            List<string> domain = new();
            string[] domains = { "@tvp.pl", "@edu.co.uk"};
            domain.AddRange(domains);

            int addError = 0;

            foreach(var check in domain) if(!user.Email.ToLower().EndsWith(check)) addError++;
            
            if(addError == domain.Count)
            {
                errors.Add(new IdentityError
                {
                    Code = "EmailDomainError",
                    Description = "Invalid email domain"
                });
            }

            return errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
        }
    }
}

标签: asp.net-mvcasp.net-coreasp.net-identity

解决方案


您收到错误消息,因为您无法取消电子邮件字段。您应该做的是首先在您的 GET 操作中填充视图模型EditUser,然后将其传递到表单所在的视图中。

因此,将您的EditUser()GET 操作更新为:

public async Task<IActionResult> EditUser()
{
    string userId = <insert logic to get user's ID>
    var user = await _userManager.FindByIdAsync(userId);
    var viewModel = new EditUserModel
    {
        FirstName = user.FirstName,
        LastName = user.LastName,
        Email = user.Email
    };
    return View(viewModel);
}

这样,用户将能够看到他们当前的电子邮件和姓名,并且他们可以选择修改他们想要的任何字段。

您当然不能相信用户不会取消电子邮件字段,因此您应该通过视图模型中的数据注释添加服务器端验证:

using UserApp.Models;
using System.ComponentModel.DataAnnotations;
namespace UserApp
{
public class EditUserModel
    {
        
        public string FirstName { get; set; }
        public string LastName { get; set; }
        [Required]
        public string Email { get; set; }
        public string Password { get; set; }
     
    }
}

然后在执行任何其他操作之前检查 POST 操作中的模型状态:

if (!ModelState.IsValid)
{
    return View();
}

如果约定是您关心的问题,我建议您指定一个仅更新电子邮件的表格。这就是大多数应用程序/网站所做的。而且您可能应该在更新之前生成一个令牌。


推荐阅读