ASP.NET Identity教程二:(用户管理)
由于我们这里使用的是 ASP.NET Identity 来管理用户,所以 User 数据实体是继承了 IdentityUser 的,我们只需要在派生类中编写需要扩展的属性即可。而在表现层,也就是 Web 应用程序这一层,我们只需要编写 ViewModel 即可,ViewModel 只在视图和控制中使用。最终在提交到服务器时,还是需要转换为数据实体 Model 的。
在“Yidosoft.Identity”项目中添加一个名称为“ViewModels”的文件夹,将所有的ViewModel 都放在该文件夹中。
1. 注册用户
注册用户是指将用户的信息添加到数据库中,是有用户在网站上自行完成的
1.1. 编写 AddUserViewModel
在“ViewModels”文件夹中添加一个名称为“AddUserViewModel”的类,然后编写如
下代码:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; namespace jsdhh2.ViewModels { public class AddUserViewModel { #region 扩展 [Display(Name = "用户名")] public string UserName { get; set; } [Display(Name = "电话(手机/固话)")] [Phone] public string PhoneNumber { get; set; } #endregion [Required] [EmailAddress] [Display(Name = "电子邮件")] public string Email { get; set; } [Required] [StringLength(100, ErrorMessage = "{0} 必须至少包含 {2} 个字符。", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "密码")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "确认密码")] [Compare("Password", ErrorMessage = "密码和确认密码不匹配。")] public string ConfirmPassword { get; set; } }
在 ViewModel 中,可以使用数据验证和注解来保证数据的正确性。
1.2. 编写 Add 方法
在 UserController 控制器中添加一个带有 HttpGet 特性的 Add()方法,用于呈现添加用户的表单信息视图。再添加一个带有 HttpPost 特性的 Add()方法,用于提交
用户信息到数据库中。完整的代码如下:
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Add(AddUserViewModel addUserModel) { if (ModelState.IsValid) { var _creatTime = DateTime.Now; //注册时间 var _headerPic = "/Content/noheaderpic.png"; //默认的显示一个头像 var user = new User { UserName = addUserModel.UserName, Email = addUserModel.Email, PhoneNumber = addUserModel.PhoneNumber, CreateTime =_creatTime, HeaderPic = _headerPic }; var result = await UserManager.CreateAsync(user, addUserModel.Password); if (result.Succeeded) { //登录 await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); // 发送包含此链接的电子邮件 string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); var callbackUrl = Url.Action("ConfirmEmail", "User", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); await UserManager.SendEmailAsync(user.Id, "确认你的帐户", "请通过单击 <a href=\"" + callbackUrl + "\">这里</a>来确认你的帐户"); //return RedirectToAction("List", "User"); return RedirectToAction("Index", "Home"); } AddErrors(result); } return View(addUserModel); }
在这里的两个 Add()方法上都添加了 AllowAnonymous 特性,表示可以被匿名访问。ValidateAntiForgeryToken 特性用于阻止 CSRF 攻击。
在这里我们使用了 CreateAsync()异步方法来向数据库添加用户信息,在UserManager 中,定义了许多的异步操作的方法,异步方法都是以 Async 结尾的。
.3. 编写 Add 视图
在”项目的“Views”“User”中添加一个名称为“Add”的视图,如图
中修改视图名称为“Add”,点击“添加”按钮。如图
第一次为空的 MVC 项目添加视图时,VS2015 会自动添加一些其它的文件。如:bootstrap.css、Site.css、Jquery 文件及一些公共视图,还有就是“_Layout.cshtml”
视图的基本样式。
这里使用 Bootstrap 为 Add.cshtml 视图布局页面。在 add.cshtml 视图中编写如下代码:
@model jsdhh2.ViewModels.AddUserViewModel @{ ViewBag.Title = "Add"; } @using (Html.BeginForm("Add", "User", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() <h4>添加用户。</h4> <hr /> @Html.ValidationSummary("", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.PhoneNumber, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.PhoneNumber, new { @class = "form-control" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" class="btn btn-default" value="保存" /> </div> </div> }
在此视图中使用了Bootstrap的class来布局表单样式。并使用ValidationSummary()方法来执行验证。
4. 测试运行结果
add.cshtml 视图编写完成之后,就可以在 Visual Studio 2015 中按下 F5 键来运行一下。打开 add.cshtml 视图,如图 7-5 所示:中可以按 F5,也可以点击调试按钮。如图 7-6 所示:
此时的图 7-6 使用的是带 HttpGet 特性的 Add()方法返回的视图页面。
现在点击一下图 7-6 的“保存”按钮,浏览器不会刷新,会执行客户端验证。如图
凡是在 ViewModel 里使用了[Required]特性,都会在没有输入任何值的情况下出
现 7-7 的非空验证。
在点击“保存”按钮时,注意浏览器的刷新情况,如果没有刷新,则就执行了客户
端验证,如果刷新了,则会执行服务器端验证。
现在在图 7-7 的表单中输入内容。如图 7-8 所示:
输入完成后,点击“保存”按钮。注意:由于这是第一次使用 Entity Framework 提交数据到数据库,所以会检查相关的数据库、表是否已经创建,如果未创建,则
就会立即创建并执行操作。
当点击“保存”按钮后,就会执行带 HttpPost 特性的 Add()方法中的代码。图 7-8 的表单数据保存成功后会转到首页(Home/Index),这也是默认路由指定的
默认路径。如图 7-9 所示:
但是也表明了我们的执行操作成功了。打开数据库查看一下,如图 7-10 所示:
在图 7-10 中可以看到,“YDIdentityDb”数据库已经创建成功了,并且还为 ASP.NETIdentity 创建相应的表结构。
与用户相关的信息是存在“AspNetUsers”表中的,打开该表可以看到我们新添加的用户信息。这也证明我们在上面所做的操作已经执行成功了。
从这些操作中可以看到,我们使用了 Entity Framework 操作持久化数据非常方便,不仅不需要编写 SQL 语句,就连数据库和表结构都不需要我们创建。另外在ASP.NET Identity 中,对用户属性的扩展是那么的方便,这在 ASP.NET Membership中,这么扩展用户属性是做不到的。
2. 用户列表
管理用户都是从用户列表开始的,在用户列表中可以查看用户信息、编辑用户信息和删除用户信息。当然也可以放个添加用户的链接用于新增用户。用户列表就
是管理用户的入口。
在用户列表视图中,我们不使用 ViewModel,直接使用 User 类即可,因为UserManager 提供了获取所有用户的方法,直接调用即可。
.2.1. 编写 List 方法
在 UserController 控制器中添加一个带有 HttpGet 特性的 List()方法,用于以列表的形式显示所有的用户。完整的代码如下:
[HttpGet]
public ActionResult List()
{
var users = UserManager.Users.ToList();
return View(users);
}
2.2. 编写 List 视图
项目的“Views”“User”文件夹中添加一个名称为“List”的视图。
@model IEnumerable<jsdhh2.Models.User> @{ ViewBag.Title = "List"; } <div class="wrapper wrapper-content"> <div class="ibox float-e-margins"> <div class="ibox-title"> <h2>用户管理</h2> <div class="ibox-tools"> <a class="collapse-link"> <i class="fa fa-chevron-up"></i> </a> </div> </div> <div class="ibox-content"> <div class="form-group"> <a class="btn btn-primary" href="/User/Add">添加</a> <button id="btnEdit" type="button" class="btn btn-info " onclick="editModel()"><i class="fa fa-pencil"></i> 编辑</button> <button id="btnDel" type="button" class="btn btn-danger " onclick="delData()"> <i class="fa fa-remove"></i> <span class="bold">删除</span> </button> <button id="btnSetRole" type="button" class="btn btn-info "><i class="fa fa-user"></i> 角色授权</button> <button id="btnResetPwd" class="btn btn-warning"><i class="fa fa-undo"></i> 重置密码</button> </div> <div class="form-group"> <div class="input-group"> <input id="txtSearchKey" type="text" class="input form-control" placeholder="搜索关键字" /> <span class="input-group-btn"> <button id="btnSearch" class="btn btn btn-primary" type="button"> <i class="fa fa-search"></i> 搜索</button> </span> </div> </div> <div class="jqGrid_wrapper"> <table id="table_list"></table> <div id="pager_list"></div> </div> </div> </div> </div> <table class="table"> <thead> <tr> <th>用户名</th> <th>电子邮件</th> <th>QQ号码</th> <th>微信号码</th> </tr> </thead> <tbody> @foreach (var user in Model) { <tr> <td>@Html.DisplayFor(m => user.UserName)</td> <td>@Html.DisplayFor(m => user.Email)</td> <td>@Html.DisplayFor(m => user.QQ)</td> <td>@Html.DisplayFor(m => user.PhoneNumber)</td> <td>@Html.DisplayFor(m => user.CreateTime) </td> <td> @Html.ActionLink("编辑", "Edit", new { id = user.Id },new { @class= "btn btn-info" }) @Html.ActionLink("删除", "Del", new { id = user.Id }, new { @class = "btn btn-danger" }) </td> </tr> } </tbody> </table>
显示效果为
这里使用了表格 table 以列表的形式的显示用户基本信息,使用了 Bootstrap 中的class=”table”来布局样式。在<table>标记上面还添加了一个链接,用于转到添加用户的页面:
<a class="btn btn-primary" href="/User/Add">添加</a>
这样可以在用户列表中直接点击“添加用户”链接转到添加用户的页面。那么此时,我们就可以在“Add()”方法中,将添加用户成功后转到的页面修改为 List 页面。打开“UserController”,修改带有 HttpPost 的 Add()方法。如下代码:
[AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Add(AddUserViewModel addUserModel) { if (ModelState.IsValid) { var user = new User { UserName = addUserModel.Email, Email = addUserModel.Email, QQNumber = addUserModel.QQNumber, WechatNumber = addUserModel.WechatNumber }; var result = await UserManager.CreateAsync(user, addUserModel.Password); if (result.Succeeded) { //登录 await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); // 发送包含此链接的电子邮件 string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); var callbackUrl = Url.Action("ConfirmEmail", "User", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); await UserManager.SendEmailAsync(user.Id, "确认你的帐户", "请通过单击 <a href=\"" + callbackUrl + "\">这里</a>来确认你的帐户"); return RedirectToAction("List", "User"); //eturn RedirectToAction("Index", "Home"); } AddErrors(result); } return View(addUserModel);
现在新建一个用户,自动就跑到所有用户列表了。
3.增加详细页面显示
先在List视图中增加详细信息按钮
<td> @Html.ActionLink("详情", "DetailsList", new { id = user.Id }, new { @class = "btn btn-info" }) @Html.ActionLink("编辑", "Edit", new { id = user.Id },new { @class= "btn btn-info" }) @Html.ActionLink("删除", "Del", new { id = user.Id }, new { @class = "btn btn-danger" }) </td>
在UserControoer中增加DetailsList方法
[HttpGet] public ActionResult DetailsList(string id) { if (string.IsNullOrWhiteSpace(id)) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest); } User user = UserManager.FindById(id); return View(user); }
然后增加一个DetailsList视图
@model jsdhh2.Models.User @{ ViewBag.Title = "List"; } <div class="wrapper wrapper-content"> <div class="ibox float-e-margins"> <div class="ibox-title"> <h2>详细信息</h2> <div class="ibox-tools"> <a class="collapse-link"> <i class="fa fa-chevron-up"></i> </a> </div> </div> <div class="ibox-content"> <div class="form-group"> <a class="btn btn-primary" href="/User/List">返回</a> </div> </div> </div> </div> <div> <hr /> <dl class="dl-horizontal"> <dt> </dt> <dd> <img src="@Model.HeaderPic" width="80" height="80" /> </dd> <dt> @Html.DisplayNameFor(model => model.UserName) </dt><dd> @Html.DisplayFor(model => model.UserName) </dd> <dt> @Html.DisplayNameFor(model => model.RealName) </dt><dd> @Html.DisplayFor(model => model.RealName) </dd> <dt> @Html.DisplayNameFor(model => model.Email) </dt><dd> @Html.DisplayFor(model => model.Email) </dd> <dt> @Html.DisplayNameFor(model => model.QQ) </dt><dd> @Html.DisplayFor(model => model.QQ) </dd> <dt> @Html.DisplayNameFor(model => model.PhoneNumber) </dt><dd> @Html.DisplayFor(model => model.PhoneNumber) </dd> <dt> @Html.DisplayNameFor(model => model.Address) </dt><dd> @Html.DisplayFor(model => model.Address) </dd> <dt> @Html.DisplayNameFor(model => model.Gender) </dt><dd> @Html.DisplayFor(model => model.Gender) </dd> <dt> @Html.DisplayNameFor(model => model.SpouseId) </dt><dd> @Html.DisplayFor(model => model.SpouseId) </dd> <dt> @Html.DisplayNameFor(model => model.Address) </dt><dd> @Html.DisplayFor(model => model.Address) </dd> <dt> @Html.DisplayNameFor(model => model.DepartmentId) </dt><dd> @Html.DisplayFor(model => model.DepartmentId) </dd> <dt> @Html.DisplayNameFor(model => model.BirthDate) </dt><dd> @Html.DisplayFor(model => model.BirthDate) </dd> <dt> @Html.DisplayNameFor(model => model.TheHour) </dt><dd> @Html.DisplayFor(model => model.TheHour) </dd> <dt> @Html.DisplayNameFor(model => model.DetailedTime) </dt> <dd> @Html.DisplayFor(model => model.DetailedTime) </dd> </div>
显示效果为
4.