ASP.NET Identity系列02,在ASP.NET MVC中增删改查用户
本篇体验在ASP.NET MVC中使用ASP.NET Identity增删改查用户。
源码在这里:https://github.com/darrenji/UseIdentityCRUDUserInMVC
在VS2013中创建一个MVC项目,用默认的"无身份验证"作为身份验证机制。
通过控制台下载Bootstrap。
Install-Package -version 3.0.3 bootstrap
下载成功后,在解决方案下的Content和Scripts多了该版本的css和js文件。
把创建项目默认HomeController中的所有Action以及/Views/Home下的所有视图删除。
热热身
先来做一个简单练习。
在HomeController中的Index方法中,把一个字典传递给视图。
public class HomeController : Controller{public ActionResult Index(){Dictionary<string, object> data = new Dictionary<string, object>();data.Add("placeholder", "placeholder");return View(data);}}
_Layout.cshtml设置如下:
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ASP.NET Identity实战</title><link href="~/Content/bootstrap.min.css" rel="stylesheet" /><link href="~/Content/bootstrap-theme.min.css" rel="stylesheet" /><style>.container {padding-top:10px;}.validation-summary-errors{color:red;}</style></head><body><div class="container">@RenderBody()</div>@Scripts.Render("~/bundles/jquery")@Scripts.Render("~/bundles/bootstrap")@RenderSection("scripts", required: false)</body>
Home/Index.cshtml视图中:
@{ViewBag.Title = "Index";Layout = "~/Views/Shared/_Layout.cshtml";}<div class="panel panel-primary"><div class="panel-heading">用户明细</div><table class="table table-striped">@foreach (string key in Model.Keys){<tr><th>@key</th><td>@Model[key]</td></tr>}</table></div>

前期准备
分别安装如下组件。
Install-Package Microsoft.AspNet.Identity.EntityFramework –Version 2.0.0
Install-Package Microsoft.AspNet.Identity.OWIN -Version 2.0.0
Install-Package Microsoft.Owin.Host.SystemWeb -Version 2.1.0
配置Web.config如下:
<?xml version="1.0" encoding="utf-8"?><!--有关如何配置 ASP.NET 应用程序的详细信息,请访问http://go.microsoft.com/fwlink/?LinkId=301880--><configuration><configSections><!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --><section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /></configSections><connectionStrings><add name="IdentityDb" providerName="System.Data.SqlClient" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=IdentityDb;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False;MultipleActiveResultSets=True"/></connectionStrings><appSettings><add key="webpages:Version" value="3.0.0.0" /><add key="webpages:Enabled" value="false" /><add key="ClientValidationEnabled" value="true" /><add key="UnobtrusiveJavaScriptEnabled" value="true" /><add key="owin:AppStartup" value="WebApplication4.IdentityConfig" /></appSettings><system.web><compilation debug="true" targetFramework="4.5" /><httpRuntime targetFramework="4.5" /></system.web><runtime><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><dependentAssembly><assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" /><bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" /></dependentAssembly><dependentAssembly><assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" /><bindingRedirect oldVersion="1.0.0.0-5.0.0.0" newVersion="5.0.0.0" /></dependentAssembly><dependentAssembly><assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" /><bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" /></dependentAssembly><dependentAssembly><assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" /><bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" /></dependentAssembly><dependentAssembly><assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" /><bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" /></dependentAssembly></assemblyBinding></runtime><entityFramework><defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" /><providers><provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /></providers></entityFramework></configuration>
以上,
● 增加了connectionStrings节点,将自动创建localdb数据库
● 在appSettings节点中增加了一个key为owin:AppStartup项,这是确保OWIN运行正常的全局配置
在Models文件夹下创建如下类。
public class AppUser : IdentityUser{}
在解决方案下创建Infrastructure文件夹。
在Infrastructure文件夹下创建一个上下文类,需要实现IdentityDbContext<>接口。
public class AppIdentityDbContext : IdentityDbContext<AppUser>{public AppIdentityDbContext(): base("IdentityDb"){}static AppIdentityDbContext(){//使用EF Code First第一次创建的时候调用Database.SetInitializer<AppIdentityDbContext>(new IdentityDbInit());}public static AppIdentityDbContext Create(){return new AppIdentityDbContext();}}//初始化public class IdentityDbInit : DropCreateDatabaseIfModelChanges<AppIdentityDbContext>{protected override void Seed(AppIdentityDbContext context){PerformInitialSetup(context);base.Seed(context);}//初始化工作public void PerformInitialSetup(AppIdentityDbContext context){ }}
在Infrastructure文件夹下创建一个管理用户的类,需要继承UserManager<AppUser>类。
还记得,先前在appSettings节点中配置了一个如下方式:
<add key="owin:AppStartup" value="WebApplication4.IdentityConfig" />
OWIN需要一个全局启动文件,默认会到项目的顶级命名空间下找IdentityConfig这个类。
那就在App_Start中创建IdentityConfig这个类,这个类在WebApplication4这个命名空间下。namespace WebApplication4{public class IdentityConfig{public void Configuration(IAppBuilder app){app.CreatePerOwinContext<AppIdentityDbContext>(AppIdentityDbContext.Create);app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);app.UseCookieAuthentication(new CookieAuthenticationOptions {AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,LoginPath = new Microsoft.Owin.PathString("/Account/Login")});}}}
显示用户
创建AdminController,现在可以向视图传递所有的用户了,编写如下:
public class AdminController : Controller{public ActionResult Index(){return View(UserManager.Users);}private AppUserManager UserManager{get{return HttpContext.GetOwinContext().GetUserManager<AppUserManager>();}}}
再创建Admin/Index.cshtml类型为IEnumerable<AppUser>的强类型视图。
@model IEnumerable<WebApplication4.Models.AppUser>@{ViewBag.Title = "Index";Layout = "~/Views/Shared/_Layout.cshtml";}<div class="panel panel-primary"><div class="panel-heading">所有用户账户</div><table class="table table-striped"><tr><th>ID</th><th>Name</th><th>Email</th><th></th></tr>@if (Model.Count() == 0){<tr><td colspan="4" class="text-center">还没有创建用户</td></tr>}else{foreach (WebApplication4.Models.AppUser user in Model){<tr><td>@user.Id</td><td>@user.UserName</td><td>@user.Email</td><td>@using (Html.BeginForm("Delete", "Admin",new { id = user.Id })){@Html.ActionLink("编辑", "Edit", new { id = user.Id },new { @class = "btn btn-primary btn-xs" })<button class="btn btn-danger btn-xs"type="submit">删除</button>}</td></tr>}}</table></div>@Html.ActionLink("创建用户", "Create", null, new { @class = "btn btn-primary" })

创建用户
在Models文件夹下创建一个视图模型。
namespace WebApplication4.Models{public class CreateModel{public string Id { get; set; }[Required]public string Name { get; set; }[Required]public string Email { get; set; }[Required]public string Password { get; set; }}}
在AdminController中添加创建用户相关的方法。
public class AdminController : Controller{public ActionResult Index(){return View(UserManager.Users);}//创建显示public ActionResult Create(){return View();}[HttpPost]public async Task<ActionResult> Create(CreateModel model){if(ModelState.IsValid){var user = new AppUser{UserName = model.Name, Email = model.Email};IdentityResult result = await UserManager.CreateAsync(user, model.Password);if(result.Succeeded){return RedirectToAction("Index");}else{AddErrorsFromResult(result);}}return View(model);}//创建接收private void AddErrorsFromResult(IdentityResult result){foreach(var error in result.Errors){ModelState.AddModelError("", error);}}private AppUserManager UserManager{get{return HttpContext.GetOwinContext().GetUserManager<AppUserManager>();}}}
在Admin/Create.cshtml视图页中:
@model WebApplication4.Models.CreateModel@{ViewBag.Title = "Create";Layout = "~/Views/Shared/_Layout.cshtml";}<h2>Create</h2>@using (Html.BeginForm()){@Html.AntiForgeryToken()<div class="form-horizontal"><h4>创建用户</h4><hr />@Html.ValidationSummary(true)<div class="form-group">@Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" })<div class="col-md-10">@Html.EditorFor(model => model.Name)@Html.ValidationMessageFor(model => model.Name)</div></div><div class="form-group">@Html.LabelFor(model => model.Email, new { @class = "control-label col-md-2" })<div class="col-md-10">@Html.EditorFor(model => model.Email)@Html.ValidationMessageFor(model => model.Email)</div></div><div class="form-group">@Html.LabelFor(model => model.Password, new { @class = "control-label col-md-2" })<div class="col-md-10">@Html.EditorFor(model => model.Password)@Html.ValidationMessageFor(model => model.Password)</div></div><div class="form-group"><div class="col-md-offset-2 col-md-10"><input type="submit" value="创建用户" class="btn btn-default" /></div></div></div>}<div>@Html.ActionLink("返回", "Index")</div>

点击"创建"按钮,创建成功返回显示用户页面。

oh, my god,只是配置了一下就有数据了? 数据在哪呢?
点击左上角的"服务器资源管理器",右键"IdentityDb",点击"刷新"。

再打开AspNetUsers表,刚创建的用户赫然在列。

好像还有点欠缺,用户输入密码的时候,总应该有些限制吧。
能想到的,ASP.NET Identity都为我们准备好了。有一个PasswordValidator类就是干这个的。
在Infrastructure文件夹中创建一个PasswordValidator类的继承子类。
namespace WebApplication4.Infrastructure{public class CustomPasswordValidator : PasswordValidator{public override async Task<IdentityResult> ValidateAsync(string pass){IdentityResult result = await base.ValidateAsync(pass);if (pass.Contains("12345")){var errors = result.Errors.ToList();errors.Add("密码中包含太多连续数字");result = new IdentityResult(errors);}return result;}}}
然后需要把这个规则告诉UserManager。
namespace WebApplication4.Infrastructure{public class AppUserManager : UserManager<AppUser>{public AppUserManager(IUserStore<AppUser> store) : base(store) { }public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context){//identity ef上下文AppIdentityDbContext db = context.Get<AppIdentityDbContext>();//与identity ef相关的UserStoreIUserStore<AppUser> us = new UserStore<AppUser>(db);AppUserManager manager = new AppUserManager(us);//密码相关manager.PasswordValidator = new CustomPasswordValidator {RequiredLength = 6,RequireNonLetterOrDigit = false,RequireDigit = false,RequireLowercase = true,RequireUppercase = true};return manager;}}}
再次运行程序,创建用户页面,尝试输入不通过的密码。

不过,关于密码的规则,似乎可以在View Model的验证层面就可以解决掉。
编辑和删除用户
在AdminController中增加编辑和删除的部分。
public class AdminController : Controller{public ActionResult Index(){return View(UserManager.Users);}//创建显示public ActionResult Create(){return View();}//创建接收[HttpPost]public async Task<ActionResult> Create(CreateModel model){if(ModelState.IsValid){var user = new AppUser{UserName = model.Name, Email = model.Email};IdentityResult result = await UserManager.CreateAsync(user, model.Password);if(result.Succeeded){return RedirectToAction("Index");}else{AddErrorsFromResult(result);}}return View(model);}//编辑显示public async Task<ActionResult> Edit(string id){AppUser user = await UserManager.FindByIdAsync(id);if(User != null){CreateModel createModel = new CreateModel();createModel.Id = user.Id;createModel.Email = user.Email;createModel.Name = user.UserName;createModel.Password = user.PasswordHash;return View(createModel);}else{return RedirectToAction("Index");}}//接收编辑[HttpPost]public async Task<ActionResult> Edit(CreateModel createModel){if(ModelState.IsValid){AppUser user = await UserManager.FindByIdAsync(createModel.Id);if (user != null){//关于邮箱user.Email = createModel.Email;IdentityResult validEmail = await UserManager.UserValidator.ValidateAsync(user);if (!validEmail.Succeeded){AddErrorsFromResult(validEmail);}user.UserName = createModel.Name;//关于密码IdentityResult validPass = null;if (createModel.Password != string.Empty){validPass = await UserManager.PasswordValidator.ValidateAsync(createModel.Password);if (validPass.Succeeded){user.PasswordHash = UserManager.PasswordHasher.HashPassword(createModel.Password);}else{AddErrorsFromResult(validPass);}}user.Email = createModel.Email;//验证结果if ((validEmail.Succeeded && validPass == null) || (validEmail.Succeeded&& createModel.Password != string.Empty && validPass.Succeeded)){IdentityResult result = await UserManager.UpdateAsync(user);if (result.Succeeded){return RedirectToAction("Index");}else{AddErrorsFromResult(result);}}else{ModelState.AddModelError("", "无此用户");}}return View(createModel);}else{return View(createModel);}}//删除[HttpPost]public async Task<ActionResult> Delete(string id){AppUser user = await UserManager.FindByIdAsync(id);if(user != null){IdentityResult result = await UserManager.DeleteAsync(user);if(result.Succeeded){return RedirectToAction("Index");}else{return View("Error", result.Errors);}}else{return View("Error", new string[] { "没有此用户" });}}private void AddErrorsFromResult(IdentityResult result){foreach(var error in result.Errors){ModelState.AddModelError("", error);}}private AppUserManager UserManager{get{return HttpContext.GetOwinContext().GetUserManager<AppUserManager>();}}}
Admin/Edit.cshtml视图。
@model WebApplication4.Models.CreateModel@{ViewBag.Title = "Edit";Layout = "~/Views/Shared/_Layout.cshtml";}<h2>Edit</h2>@using (Html.BeginForm()){@Html.AntiForgeryToken()<div class="form-horizontal"><hr />@Html.ValidationSummary(true)@Html.HiddenFor(model => model.Id)<div class="form-group">@Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" })<div class="col-md-10">@Html.EditorFor(model => model.Name)@Html.ValidationMessageFor(model => model.Name)</div></div><div class="form-group">@Html.LabelFor(model => model.Email, new { @class = "control-label col-md-2" })<div class="col-md-10">@Html.EditorFor(model => model.Email)@Html.ValidationMessageFor(model => model.Email)</div></div><div class="form-group">@Html.LabelFor(model => model.Password, new { @class = "control-label col-md-2" })<div class="col-md-10">@Html.EditorFor(model => model.Password)@Html.ValidationMessageFor(model => model.Password)</div></div><div class="form-group"><div class="col-md-offset-2 col-md-10"><input type="submit" value="保存" class="btn btn-default" /></div></div></div>}<div>@Html.ActionLink("返回", "Index")</div>
另外,如果删除失败,跳转到Shared/Error.cshtml视图页。
@model IEnumerable<string>@{ ViewBag.Title = "Error";}<div class="alert alert-danger">@switch (Model.Count()){case 0:@: Something went wrong. Please try againbreak;case 1:@Model.First();break;default:@: 发现如下错误:<ul>@foreach (string error in Model){<li>@error</li>}</ul>break;}</div>@Html.ActionLink("确定", "Index", null, new { @class = "btn btn-default" })
至此,使用ASP.NET Identy实现对用户的增删改查完毕,ASP.NET Identity真的很好很强大!
ASP.NET Identity系列02,在ASP.NET MVC中增删改查用户的更多相关文章
- 【ASP.NET MVC系列】浅谈jqGrid 在ASP.NET MVC中增删改查
ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...
- ABP入门系列(6)——展现层实现增删改查
这一章节将通过完善Controller.View.ViewModel,来实现展现层的增删改查.最终实现效果如下图: 一.定义Controller ABP对ASP.NET MVC Controllers ...
- ABP入门系列(5)——展现层实现增删改查
ABP入门系列目录--学习Abp框架之实操演练 这一章节将通过完善Controller.View.ViewModel,来实现展现层的增删改查.最终实现效果如下图: 一.定义Controller ABP ...
- ABP学习入门系列(五)(展示实现增删改查)
大致要实现的 效果如下 1,添加Controller(用到的X.PagedList 注意到nuget添加) using System.Web.Mvc; using Abp.Application.Se ...
- Elasticsearch学习系列之单模式下API的增删改查操作
这里我们通过Elasticsearch的marvel插件实现单模式下API的增删改查操作 索引的初始化操作 创建索引之前可以对索引进行初始化操作,比如先指定shard数量以及replicas的数量 代 ...
- 【ASP.NET MVC】jqGrid 增删改查详解
1 概述 本篇文章主要是关于JqGrid的,主要功能包括使用JqGrid增删查改,导入导出,废话不多说,直接进入正题. 2 Demo相关 2.1 Demo展示 第一部分 第二部分 2.2 ...
- ASP.NET Identity系列教程(目录)
$(document).ready(function(){ $("#hide").click(function(){ $(".en").hide(); }); ...
- 【ASP.NET Identity系列教程(一)】ASP.NET Identity入门
注:本文是[ASP.NET Identity系列教程]的第一篇.本系列教程详细.完整.深入地介绍了微软的ASP.NET Identity技术,描述了如何运用ASP.NET Identity实现应用程序 ...
- 【ASP.NET Identity系列教程(二)】运用ASP.NET Identity
注:本文是[ASP.NET Identity系列教程]的第二篇.本系列教程详细.完整.深入地介绍了微软的ASP.NET Identity技术,描述了如何运用ASP.NET Identity实现应用程序 ...
随机推荐
- Linux中Samba详细安装【转】
转自:http://www.cnblogs.com/whiteyun/archive/2011/05/27/2059670.html 为了实现Windows主机与Linux服务器之间的资源共享,Lin ...
- Google-Guice入门介绍
原地址:http://blog.csdn.net/derekjiang/article/details/7231490 一. 概述 Guice是一个轻量级的DI框架.本文对Guice的基本用法作以介绍 ...
- google closure 笔记-SOY template
一 使用js模板 closure template 目前支持Java和js.但是模板语法的设计不依赖于任何现成的语言,所以理论上可以支持任何语言,只是暂时只有java编译器. 使用js模板:编写模板文 ...
- 可视化并理解CNN
参考:https://zhuanlan.zhihu.com/p/24833574 学习论文[1311.2901] Visualizing and Understanding Convolutional ...
- Coursera台大机器学习技法课程笔记15-Matrix Factorization
很多ML模型用的都是数值特征,那么对于分类特征,该怎么做呢? 以linear network为例:先对特征进行转换,转换成有意义的特征后,再对其进行线性组合 进一步,模型可表示为:使Ein最小,我们就 ...
- javaweb笔记七
过滤器:是一个web中间组件,用于拦截从客户端发送给服务器的请求和响应.当客户端向服务器发出请求时,服务器会查看是否有过滤器和该请求匹配,如果有,则交给过滤器执行,业务操作后,可以将请求继续向目标资源 ...
- 第二届CCF软件能力认证
1. 相邻数对 问题描述 给定n个不同的整数,问这些数中有多少对整数,它们的值正好相差1. 输入格式 输入的第一行包含一个整数n,表示给定整数的个数. 第二行包含所给定的n个整数. 输出格式 输出一个 ...
- 为什么不要在Spring的配置里,配置上XSD的版本号
为什么不要在Spring的配置里,配置上XSD的版本号?因为如果没有配置版本号,取的就是当前jar里的XSD文件,减少了各种风险.而且这样约定大于配置的方式很优雅.
- Codeforces Round #533 (Div. 2) E - Helping Hiasat 最大团
E - Helping Hiasat 裸的最大团,写了一种 2 ^ (m / 2) * (m / 2)的复杂度的壮压, 应该还有更好的方法. #include<bits/stdc++.h> ...
- MySQL DROP DB或TABLE场景下借助SQL Thread快速应用binlog恢复方案
[问题] 假设有这种场景,误操作DROP DB或TABLE,常规的恢复操作是还原全备份,并用mysqlbinlog追加到drop操作前的位置. 如果需要恢复的binlog的日志量比较大而我们只希望恢复 ...