推荐关注「码侠江湖」加星标,时刻不忘江湖事
这是 ASP.NET Core Identity 系列的第二篇文章,上一篇文章介绍了 Identity 框架的集成,以及一些基础知识。
这篇文章讲一讲如何在 ASP.NET Core Identity 中实现用户注册。
点击上方或后方蓝字,阅读 ASP.NET Core Identity 系列合集。
本篇文章的示例项目:https://github.com/zilor-net/IdentitySample/tree/main/Sample02
准备工作
ASP.NET Core Identity 提供了很多不同的身份选项,帮助我们实现用户注册。
首先,让我们在 「Models」 文件夹中,创建一个用户注册模型:
public class UserRegistrationModel
{
[Display(Name = "姓氏")]
public string FirstName { get; set; }
[Display(Name = "名字")]
public string LastName { get; set; }
[Display(Name = "电子邮箱")]
[Required(ErrorMessage = "电子邮箱不能为空")]
[EmailAddress]
public string Email { get; set; }
[Display(Name = "密码")]
[Required(ErrorMessage = "密码不能为空")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "确认密码")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "密码与确认密码不匹配。")]
public string ConfirmPassword { get; set; }
}
这个类中的属性,需要用户在注册表单中填写。
其中,Email
和 Password
属性是必需的,ConfirmPassword
属性的值必须与 Password
属性的值匹配。
有了这个模型,我们再来创建一个 「Account」 控制器:
public class AccountController : Controller
{
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Register(UserRegistrationModel userModel)
{
return View();
}
}
它有两个 Register
操作方法,用来提供用户注册功能。
第一个 Register
方法接受 Get 请求,用来显示注册表单的视图;
第二个 Register
方法接受 Post 请求,用来处理用户注册逻辑,并显示注册结果的视图。
还需要为 GET Register
操作,创建一个视图,这里我们可以直接使用 Create
模板,模型类选择 UserRegistrationModel
类即可。
![5a8f805151b658c18bcc9cf63938bb4c.png](https://i-blog.csdnimg.cn/blog_migrate/03032b7b75ca3d8e4d59a72faa796794.png)
现在这个视图中的表单,已经可以为用户注册模型,提供所有的输入字段了。
需要注意的是,我们最好修改表单中 asp-validation-summary
的值为 All
。
因为,默认情况下,它只能显示模型验证产生的错误信息,而不会显示我们自己设置的验证错误。
视图中的 Create 按钮,会跳转到 POST 请求的 Register
操作方法,并且填充用户注册模型。
在 Web API 中使用来自用户的数据,最好采用数据传输对象的方式,但是在 MVC 中不一定如此。
这是因为 MVC 有模型的存在,在有视图的情况下,模型在一定程度上,替代了数据传输对象职责。
由于我们的数据通过视图传递,因此该类模型也被称为视图模型。
「UserRegistrationModel」 就是一个视图模型,如果我们想要真正的在数据库中存储它,必须要把它映射到 User
实体类。
我们需要安装 AutoMapper :
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
然后,在 Program
类中注册它:
builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());
接着,在布局页的导航菜单上,添加一个注册按钮。
因为后面还要添加登录按钮,所以为了代码的可维护性,我们可以创建一个分部视图:
// Shared\_LoginPartial.cshtml
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link text-dark" asp-controller="Account"
asp-action="Register">注册</a>
</li>
</ul>
最后,修改 「_Layout.cshtml」 布局视图,在导航菜单上添加登录分布视图:
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<partial name="_LoginPartial" /><!-- 添加 -->
<ul class="navbar-nav flex-grow-1">
注册操作
准备工作都已经完成了。
现在,让我们从用户注册逻辑开始。
首先,必须在 「Account」 控制器中,注入 AutoMapper
和 UserManager
类:
private readonly IMapper _mapper;
private readonly UserManager<User> _userManager;
public AccountController(IMapper mapper, UserManager<User> userManager)
{
_mapper = mapper;
_userManager = userManager;
}
「UserManager」 由 Identity 框架提供,它负责帮助我们管理应用程序中的用户。
用户注册逻辑在 Post 请求的 Register
方法中实现:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(UserRegistrationModel userModel)
{
if(!ModelState.IsValid)
{
return View(userModel);
}
var user = _mapper.Map<User>(userModel);
var result = await _userManager.CreateAsync(user, userModel.Password);
if(!result.Succeeded)
{
foreach (var error in result.Errors)
{
ModelState.TryAddModelError(error.Code, error.Description);
}
return View(userModel);
}
await _userManager.AddToRoleAsync(user, "Guest");
return RedirectToAction(nameof(HomeController.Index), "Home");
}
这里需要注意的是,我们把这个方法改成了异步的,因为 ·UserManager· 的助手方法也是异步的。
在方法内部,先检查模型的有效性,如果它是无效的,就返回带有无效模型的相同视图。
如果检查通过,就将 userModel
射到 user
实体。
使用 CreateAsync
方法用来注册用户,它会对密码进行哈希后保存。
之后,检查它返回的创建结果。
如果创建成功,我们只需在 UserManager
的帮助下,使用 AddToRoleAsync
方法,给用户添加一个默认的角色,并将用户重定向到 Index
页面。
但是如果注册失败,就遍历所有的错误,并将它们添加到 ModelState
中。
前面我们已经将视图的验证信息摘要改为了 All
,否则这里添加的错误,不会显示出来。
最后,不要忘记创建 AutoMapper
的映射配置类:
// MappingProfile.cs
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<UserRegistrationModel, User>()
.ForMember(
user => user.UserName,
expression => expression.MapFrom(userModel => userModel.Email));
}
}
这里我们把电子邮件映射到用户名,因为我们在注册表单中没有提供用户名的字段。
现在启动应用,测试一下用户注册。
我可以尝试各种错误的注册内容,观察验证结果。
需要注意的是,默认的密码验证规则,非常的复杂,它不但要求有大小写字母,还必须要求你有一个特殊符号。
这个密码规则很不错,但是有时候我可能并不需要强规则。
而且我们使用了电子邮件作为用户名,但是默认情况下,没有要求电子邮件是唯一的。
所以,我们可以改变这些规则,这需要在注册 Identity 的地方,通过配置选项修改:
services.AddIdentity<User, IdentityRole>(options =>
{
options.Password.RequiredLength = 6;
options.Password.RequireDigit = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireLowercase = false;
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<ApplicationContext>();
比如,我们这里取消了数字、大写字母和特殊字符的验证,最小长度改为 6 位,同时设置电子邮件的唯一验证。
现在我们再来启动应用,测试一下注册过程,比如错误的密码格式、重复的电子邮箱。
大家通过一些测试可以发现,有些错误信息是中文的、有些则是英文的。
因为这个错误分为两种类型,一种是我们自定义的模型验证错误,我们已经设置了错误的信息,所以这种类型是中文的。
另一种是 ASP.NET Core Identity 内置的验证错误,它是我们通过在操作方法中,自己读取并添加进模型状态的。
因为 ASP.NET Core Identity 是英文版的,所以我们获取到的是英文信息,
不过,我们也可以自行定义中文信息,这需要我们创建一个自定义的错误描述类:
public class CustomIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError PasswordTooShort(int length)
{
return new()
{
Code = nameof(PasswordTooShort),
Description = $"密码至少需要 {length} 位"
};
}
public override IdentityError DuplicateEmail(string email)
{
return new()
{
Code = nameof(DuplicateUserName),
Description = $"电子邮箱 {email} 已存在。"
};
}
public override IdentityError DuplicateUserName(string username)
{
return new()
{
Code = nameof(DuplicateUserName),
Description = $"用户名 {username} 已存在。"
};
}
自定义的错误信息,需要通过覆写 「IdentityErrorDescriber」 基类的错误方法,设置你想要的错误描述。
需要的话,你可以通过阅读源码,找到所有的错误方法。
我们还需要把它配置到注册 Identity 服务的方法中:
AddErrorDescriber<CustomIdentityErrorDescriber>()
小结
现在,我们已经实现了用户注册,具体的代码可以参看示例项目,下篇文章将会继续讲解用户登陆以及身份认证。
更多精彩内容,请关注我▼▼
如果喜欢我的文章,那么
在看和转发是对我最大的支持!
(戳下面蓝字阅读)
推荐关注微信公众号:码侠江湖
觉得不错,点个在看再走哟