背景:用过abp框架的应该都知道,abp框架会封装一些成熟的类方法提供给使用者来使用,用来操作 一些内置的实体类(类中一些字段设置为了protected internal),比如IdentityUser、IdentityRole等。但是这些封装的方法总是会有各种各样的校验,当然如果我们没有特殊需求,可以直接使用它提供的方法,本文就是基于创建、更新用户信息时,用户名重复的校验所写,像密码规则的校验,这里预留了参数来操作校验与否。
1.在Domain层创建UserManager
这里我们继承了UserManager的泛型类,这是为了偷个懒,毕竟不是要重写所有的方法,其他的方法我们可继续使用。
创建用户
public virtual async Task<IdentityResult> CreateAsync(IdentityUser user, string password, bool validatePassword = false)
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
if (string.IsNullOrEmpty(password))
{
throw new ArgumentNullException(nameof(password));
}
var result = await SetPasswordHashAsync(user, password,validatePassword);
if (!result.Succeeded)
{
return result;
}
return await CreateAsync(user);
}
public override async Task<IdentityResult> CreateAsync(IdentityUser user)
{
this.ThrowIfDisposed();
await SetLockoutEnabledAsync(user, true);
await UpdateNormalizedUserNameAsync(user);
await UpdateNormalizedEmailAsync(user);
return await Store.CreateAsync(user, this.CancellationToken);
}
这里我们写了 两个接口:
第一个:看参数很明显,这个是需要带着密码来创建的,里面校验了一些基本信息,其中validatePassword就是设置是否按照设定的密码规则进行校验。
Configure<IdentityOptions>(options =>
{
options.User.RequireUniqueEmail = false;
options.User.AllowedUserNameCharacters = "";
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredLength = 8;
});
这段代码的配置可以放在api.host的module里,用来配置密码、邮箱、还有用户名格式的限制。
options.User.AllowedUserNameCharacters = "";特殊说明一下,设置为空后,用户名就可以任意写了,否则abp有个默认的字符集限制,“abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+”,也就是你的用户名只能从这些字符里选,否则校验就会失败。
第二个:这个里面就什么校验都没有了。
更新用户
/// <summary>
/// Updates the specified <paramref name="user" /> in the backing store.
/// </summary>
/// <param name="user">The user to update.</param>
/// <returns>
/// The <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation, containing the <see cref="T:Microsoft.AspNetCore.Identity.IdentityResult" />
/// of the operation.
/// </returns>
public override async Task<IdentityResult> UpdateAsync(IdentityUser user)
{
this.ThrowIfDisposed();
if(user==null)
{
throw new ArgumentNullException(nameof (user));
}
await this.UpdateNormalizedUserNameAsync(user);
await this.UpdateNormalizedEmailAsync(user);
return await this.Store.UpdateAsync(user, this.CancellationToken);
}
public async Task<IdentityResult> SetPasswordHashAsync(IdentityUser user,string password, bool validatePassword = false)
{
return await UpdatePasswordHash(user, password, validatePassword);
}
这里有两个方法:
第一个:更新用户信息。
第二个:更新用户的密码。
这里单独更新密码,也是因为更新用户信息不一定就更新密码,所以预留个接口,需要的时候调用即可。
2.体验效果
写完后我们体验一下使用效果,随便写个service,注入我们写的UserManager:
我写了三个简单的方法:
public async Task<IdentityUser> GetUserByNameAsync(string userName)
{
var user =await _userManager.FindByNameAsync(userName);
return user;
}
public async Task<IdentityResult> CreateUserAsync(string userName, string password,string phoneNumber)
{
var user = new IdentityUser(Guid.NewGuid(), userName,$"{userName}@bookstore.com");
user.SetPhoneNumber(phoneNumber,true);
return await _userManager.CreateAsync(user, "bookStore12345..");
}
public async Task<IdentityResult> UpdateUserAsync(Guid userId,string userName, string password,string phoneNumber)
{
var user = await _userManager.FindByIdAsync(userId.ToString());
user.SetPhoneNumber(phoneNumber,true);
await _userManager.SetUserNameAsync(user, userName);
await _userManager.SetPasswordHashAsync(user, password);
return await _userManager.UpdateAsync(user);
}
调用创建方法:
完全相同的参数,我们这里调用了两次,都是成功。
数据库的记录也是有相同用户名的两条。创建没有问题,我们试下更新。
如果相同用户名的有多条,那么调用userManager.FindByNameAsync方法时会报错吗?如果不报错那么会返回哪条记录呢?下面看下调用效果。下面图中的效果是我调用接口10次返回的同样一个结果,都是最早创建的那个用户。
这里创建和修改时的用户名重复校验取消后,我们尽量就不要再去调用FindByNameAsync方法了,虽然它不报错,但是返回的是默认第一个创建的这个用户名的用户。
调用更新方法:
更新需要传入id,这里我们调用了两次,都成功了。看下数据库效果:
没有什么问题,更新成功了。