调用userManager.AddToRolesAsync传入不存在角色时IdentityResult.Succeeded为true?
关于AddToRolesAsync传入不存在角色仍返回Succeeded=true的问题
这不是Bug,而是ASP.NET Identity(你使用的Microsoft.AspNet.Identity.Core版本)的默认设计行为。我来给你拆解下原因和解决办法:
为什么会这样?
默认的AddToRolesAsync实现并不会预先验证角色是否存在——它的核心逻辑只是尝试在用户与角色的关联表中添加记录。如果角色不存在,这个关联操作本身不会抛出错误或返回失败(因为Identity并没有强制要求角色必须预先存在才能建立关联,有些场景下开发者可能会先关联再创建角色,或者使用延迟创建的逻辑)。所以即便你传入了GOD这种不存在的角色,方法依然会返回Succeeded=true,只是实际上并没有创建任何有效的用户-角色关联。
如何实现你预期的“角色不存在则返回失败”?
如果你希望严格验证角色必须存在才能添加,需要手动添加检查逻辑,有两种常用方式:
方式1:调用AddToRolesAsync前手动检查
在调用方法前,先通过RoleManager验证所有传入的角色是否存在:
// 假设你已经注入了RoleManager<IdentityRole> var targetRoles = new[] { "GOD", "admin" }; // 获取系统中所有已存在的角色名称 var existingRoleNames = await _roleManager.Roles.Select(r => r.Name).ToListAsync(); // 找出不存在的角色 var missingRoles = targetRoles.Except(existingRoleNames).ToList(); if (missingRoles.Any()) { // 返回失败的IdentityResult,自定义错误信息 return IdentityResult.Failed(new IdentityError { Code = "RoleNotFound", Description = $"以下角色不存在:{string.Join(", ", missingRoles)}" }); } // 所有角色都存在,执行添加操作 var result = await _userManager.AddToRolesAsync(user, targetRoles);
方式2:自定义UserManager重写AddToRolesAsync
如果你的系统多处需要这个验证逻辑,可以自定义UserManager,把检查逻辑内置进去,避免重复代码:
public class CustomUserManager : UserManager<ApplicationUser> { private readonly RoleManager<IdentityRole> _roleManager; // 构造函数需要注入RoleManager等依赖 public CustomUserManager( IUserStore<ApplicationUser> store, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators, IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<ApplicationUser>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) { _roleManager = roleManager; } public override async Task<IdentityResult> AddToRolesAsync(ApplicationUser user, IEnumerable<string> roles) { var missingRoles = new List<string>(); foreach (var role in roles) { if (!await _roleManager.RoleExistsAsync(role)) { missingRoles.Add(role); } } if (missingRoles.Any()) { return IdentityResult.Failed(new IdentityError { Code = "InvalidRoles", Description = $"无法找到以下角色:{string.Join(", ", missingRoles)}" }); } // 调用基类的默认逻辑完成添加 return await base.AddToRolesAsync(user, roles); } }
之后在Startup中注册这个自定义的UserManager,替换默认实现即可。
总结
旧版的Microsoft.AspNet.Identity.Core确实没有默认开启角色存在验证,这是设计上的灵活性考虑。如果你需要严格的验证,按照上面的方法添加检查逻辑就能实现你预期的结果。
内容的提问来源于stack exchange,提问作者DotNetRussell
相关产品推荐
相关产品推荐

