ASP.NET Core更新用户(含/不含密码)模型复用与Json异常问题
问题解决方案
问题1:模型校验规则冲突解决
核心原因是同一个DTO同时适配两个业务场景导致校验规则冲突,有两种可行方案:
- 方案1(推荐):拆分不同场景的DTO
拆分出两个独立的模型:CreateUserModel(用于新建用户)和UpdateUserModel(用于更新用户)。CreateUserModel的密码字段加[Required]特性,UpdateUserModel的密码字段不加强制必填校验,只保留长度、一致性校验即可。该方案耦合度最低,后续业务规则变更时维护成本最低。 - 方案2:实现条件校验
如果不想拆分模型,可以让RegisterModel实现IValidatableObject接口,在Validate方法中根据场景判断是否需要校验密码必填:public class RegisterModel : IValidatableObject { // 原有属性保持不变 public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { // Id为空说明是新建用户场景,密码必填 if (string.IsNullOrEmpty(Id) && string.IsNullOrEmpty(Password)) { yield return new ValidationResult("密码为必填项", new[] { nameof(Password) }); } // 密码不为空时,校验确认密码是否一致 if (!string.IsNullOrEmpty(Password) && Password != ConfirmPassword) { yield return new ValidationResult("两次输入的密码不一致", new[] { nameof(ConfirmPassword) }); } } }
问题2:JSON反序列化异常解决
异常的直接原因是接口返回格式不统一:你当前NotFound返回的是纯文本字符串,且代码存在逻辑漏洞可能触发服务器返回非JSON格式的错误内容,前端统一将响应体反序列化为RegisterResult类型时解析失败。
修复步骤如下:
- 修正用户存在性判断逻辑,原逻辑判断
model.Email是否为空完全错误,应该先判断查询出来的existingUser是否为null - 所有接口返回值统一为
RegisterResult格式,不要返回纯文本 - 移除冗余的导航属性赋值和重复哈希逻辑,避免EF Core跟踪冲突
修改后的UpdateUser方法参考:
public async Task<IActionResult> UpdateUser([FromBody] RegisterModel model) { ApplicationUser existingUser = await this.UserManager.FindByIdAsync(model.Id); // 先判断用户是否存在,统一返回JSON格式 if (existingUser == null) { return NotFound(new RegisterResult { Successful = false, Errors = new List<string> { "User not found!" } }); } IList<string> existingRoles = await this.UserManager.GetRolesAsync(existingUser); if (existingRoles.Any()) { await this.UserManager.RemoveFromRoleAsync(existingUser, existingRoles.FirstOrDefault()); } await this.UserManager.AddToRoleAsync(existingUser, model.UserRoles); // 不要手动给UserRoles导航属性赋值,UserManager已经处理了角色关联 existingUser.Email = model.Email; existingUser.FirstName = model.FirstName; existingUser.LastName = model.LastName; existingUser.ProfilePicture = model.ProfilePicture; if (!string.IsNullOrEmpty(model.Password)) { // 不要重复哈希,模型的Compare特性已经保证两次密码一致,仅需哈希一次即可 existingUser.PasswordHash = this.PasswordHasher.HashPassword(existingUser, model.Password); } IdentityResult result = await this.UserManager.UpdateAsync(existingUser); if (!result.Succeeded) { var errors = result.Errors.Select(x => x.Description); return BadRequest(new RegisterResult { Successful = false, Errors = errors }); } return Ok(new RegisterResult { Successful = true }); }
内容的提问来源于stack exchange,提问作者10101
相关产品推荐
相关产品推荐

