当前位置: 代码迷 >> 综合 >> ASP.NET MVC4 菜鸟项目之路(一)改造示例代码扩展用户信息管理
  详细解决方案

ASP.NET MVC4 菜鸟项目之路(一)改造示例代码扩展用户信息管理

热度:85   发布时间:2024-01-10 02:18:45.0

http://blog.csdn.net/hanqilin/article/details/15811235

模板创建示例项目

MVC的基础内容我就不说了,入门建议看看官方的MvcMovie示例。

提供个链接(共9篇):http://www.cnblogs.com/powertoolsteam/archive/2012/11/01/2749906.html

      打开VS2012,【新建项目】,选择【ASP.NET MVC 4 Web应用程序】,名称叫MyMvc(这随便取,但常规是公司.项目的命名空间),按【确定】,模板选【Internet应用程序】(右边有说明文字:带有使用窗体身份验证的帐户控制器的默认 ASP.NET MVC 4 项目。是的,就用它自带的身份验证,而且它还支持OAuth),其它默认,按【确定】完成项目的创建。然后点运行可以看看效果如下图:

图1 初始主页

可以看到这个模板示例已经实现了一些基本功能,包括注册和登录,可以试着注册一下(这里注册为wood),成功后自动转为登录状态,见下图:

图2 登录状态

点击wood这个账号名,可进入账户管理页面,如下图:

图3 管理界面

可以看到Internet Application模板创建出的示例省掉了我们很多初始化工作,而往往我看到的一些项目例子都是自己重新实现一次账户管理(包含注册、登录、维护)。当然,这是通用的,想要添加自定义用户信息:Email、密保问题、密保答案、注册时间,上次登录时间等,那就要对代码进行改动。在改动之前,我们先做几个事情:

1、改动WebConfig的数据库连接(这不是必须的)

打开WebConfig,找到connectString节点,可以看到示例默认创建一个LocalDB数据库,这个不是很直观,修改DefaultConnection连接字符串即可指向我们自己的数据库(安装VS2012后自带的SQLEXPRESS,本来我想换成mySql的,但是CodeFirst报错,搜索了解决不了),改后别忘了启动SQL Server服务

  <connectionStrings><add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MyMvc-20130905000410;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MyMvc-20130905000410.mdf" providerName="System.Data.SqlClient" /></connectionStrings>
 <connectionStrings><add name="DefaultConnection" connectionString="Data Source=STREAM-PC\SQLEXPRESS;Initial Catalog=MyMvc;Integrated Security=SSPI;Pooling=False;Persist Security Info=true" providerName="System.Data.SqlClient" /></connectionStrings>

改了之后,再次编译运行,重新注册一次,发现能注册成功的。这时,我们在VS里面,点击菜单【视图】-【SQL server对象资源管理器】,右键点击【SQL Server】节点,选择【添加SQL Server】完成后,发现已经多了MyMvc的数据库,展开后可以看到自动创建了几个表,其中dbo.UserProfile就是存放我们的账户名的,如下图:

图4 数据库表

明眼的人一看,发现有ID和账户名,密码呢?密码去哪了?它存放在dbo.webpages_Membership里,至于为什么这样的规则,打开Filters文件夹下的 InitializeSimpleMembershipAttribute.cs 文件的41行。

WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);

可以看到,第二、三、四个参数分别为用户表名称、ID字段名称和登录名字段名称,已经默认定义,可以自行定义至于还有几个表,有什么用,就是后续要说明的内容。

2、OAuth相关(知识点介绍,可跳过)

OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。同时,任何第三方都可以使用OAUTH认证服务,任何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP、JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间,因而OAUTH是简易的。互联网很多服务如Open API,很多大公司如Google,Yahoo,Microsoft等都提供了OAUTH认证服务,这些都足以说明OAUTH标准逐渐成为开放资源授权的标准。

简单说明,大家都应该试过注册一个网站的时候,可以选择用人人网、新浪微博、QQ账号登录吧?其实它们也就实现了OAuth规范,VS打开解决方案,发现在App_Start目录创建了一个名为AuthConfig.cs的文件

打开可以看到以下内容:

public static class AuthConfig{public static void RegisterAuth(){// 若要允许此站点的用户使用他们在其他站点(例如 Microsoft、Facebook 和 Twitter)上拥有的帐户登录,// 必须更新此站点。有关详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=252166//OAuthWebSecurity.RegisterMicrosoftClient(//    clientId: "",//    clientSecret: "");//OAuthWebSecurity.RegisterTwitterClient(//    consumerKey: "",//    consumerSecret: "");//OAuthWebSecurity.RegisterFacebookClient(//    appId: "",//    appSecret: "");//OAuthWebSecurity.RegisterGoogleClient();
        }}

这些是ASP.NET MVC4带来的新的Membership系统的内容,从该文件名称可以看到,该模板示例默认实现了能让用户用外部提供方的证书(比如Facebook, Twitter, Microsoft,或Google)登陆方式,然后将源自那些提供方的一些信息集成进你的web应用,只是它们都做了注释,所以没有外部提供者被启用,也就是上图3 管理界面最下方提示的内容。假如我们要使用这些证书,只要反注释,填入相应的信息即可,只是这些都不符合中国国情,有兴趣可以尝试通过新浪微博API来测试实现,当成功以后,表dbo.webpages_OAuthMembership就会有记录了,这次我就不做演示了,因为我只想实现普通账户权限验证,而不需要用到外部资源验证。在这里,我们只要好好利用WebSecurity就是了,功能很强大,其方法描述如下。

3、代码改造扩展信息

 打开AccountController.cs,找到Register方法,可以看到注册项调用了上面所说的WebSecurity实现用户创建和登录,

WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);

我们要好好利用,但不需要改动它们,要添加自己的用户信息,主要是修改示例自动生成的两个文件:AccountModels.cs、AccountController.cs.古有曹植七步成诗,这里也七步完成改造。

第一步:在AccountModels.cs, 增加一个名为ExtraUserInfo的新类。这个类代表了将在数据库创建的新表。

[Table("ExtraUserInfo")]public class ExternalUserInfo{[Key][DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]public int Id { get; set; }[Required]public int UserId { get; set; }/// <summary>/// 用户组Id/// </summary>[Display(Name = "用户组Id")]public int GroupId { get; set; }/// <summary>/// Email/// </summary>[Display(Name = "Email", Description = "请输入您常用的Email。")][Required(ErrorMessage = "×")]public string Email { get; set; }/// <summary>/// 密保问题/// </summary>[Display(Name = "密保问题", Description = "请正确填写,在您忘记密码时用户找回密码。4-20个字符。")][Required(ErrorMessage = "×")][StringLength(20, MinimumLength = 4, ErrorMessage = "×")]public string SecurityQuestion { get; set; }/// <summary>/// 密保答案/// </summary>[Display(Name = "密保答案", Description = "请认真填写,忘记密码后回答正确才能找回密码。2-20个字符。")][Required(ErrorMessage = "×")][StringLength(20, MinimumLength = 2, ErrorMessage = "×")]public string SecurityAnswer { get; set; }/// <summary>/// 注册时间/// </summary>public DateTime? RegTime { get; set; }/// <summary>/// 上次登录时间/// </summary>public DateTime? LastLoginTime { get; set; }}

 然后把RegisterModel改为继承它,即把 

public class RegisterModel

替换为下面(要记得加上[NotMapped],不然codefirst父类映射错误) 

[NotMapped]
public class RegisterModel : ExternalUserInfo

第二步:在UsersContext类里,增加一静态UsersContext变量,这使得我们每次调用的时候不需要都创建实例,减少资源浪费,同时创建一个DbSet属性ExternalUserInfos,如下所示。

 

public class UsersContext : DbContext{public static UsersContext Instance = new UsersContext();public UsersContext(): base("DefaultConnection"){}public DbSet<UserProfile> UserProfiles { get; set; }public DbSet<ExternalUserInfo> ExternalUserInfos { get; set; }}

 

第三步:创建【Domain】文件夹,其下创建【Repository】文件夹,文件夹下创建一个仓储接口: 

 public interface IBaseRepository<T>where T : class{bool Add(T entity);bool Delete(T entity);bool Delete(int id);T Find(int id);IQueryable<T> Load(Func<T, bool> whereLambda);IQueryable<T> LoadPage<S>(int pageIndex, int pageSize, out int total, Func<T, bool> whereLambda, bool isAsc, Func<T, S> orderByLambda);bool Update(T entity);}
第四步:创建仓储基类,实现接口(注:其中Delete和Find我定义为virtual方式,是为了适应不同的表结构,如果每个表统一规范ID为Key,可以采用反射的方式直接在基类实现好)
 public class BaseRepository<T> : IBaseRepository<T> where T : class{public ResponseDbContext dbContext = ResponseDbContext.Instance;// 实现对数据库的添加功能,添加实现EF框架的引用public bool Add(T entity){dbContext.Entry<T>(entity).State = EntityState.Added;//下面的写法统一return dbContext.SaveChanges() > 0;}//实现对数据库的修改功能public bool Update(T entity){dbContext.Set<T>().Attach(entity);dbContext.Entry<T>(entity).State = EntityState.Modified;return dbContext.SaveChanges() > 0;}//实现对数据库的删除功能public bool Delete(T entity){dbContext.Set<T>().Attach(entity);dbContext.Entry<T>(entity).State = EntityState.Deleted;return dbContext.SaveChanges() > 0;}public virtual bool Delete(int id) { return false; }public virtual T Find(int id){ return null; }//实现对数据库的查询  --简单查询public IQueryable<T> Load(Func<T, bool> whereLambda){return dbContext.Set<T>().Where<T>(whereLambda).AsQueryable();}/// <summary>/// 实现对数据的分页查询/// </summary>/// <typeparam name="S">按照某个类进行排序</typeparam>/// <param name="pageIndex">当前第几页</param>/// <param name="pageSize">一页显示多少条数据</param>/// <param name="total">总条数</param>/// <param name="whereLambda">取得排序的条件</param>/// <param name="isAsc">如何排序,根据倒叙还是升序</param>/// <param name="orderByLambda">根据那个字段进行排序</param>/// <returns></returns>public IQueryable<T> LoadPage<S>(int pageIndex, int pageSize, out  int total, Func<T, bool> whereLambda,bool isAsc, Func<T, S> orderByLambda){var temp = dbContext.Set<T>().Where<T>(whereLambda);total = temp.Count(); //得到总的条数//排序,获取当前页的数据if (isAsc){temp = temp.OrderBy<T, S>(orderByLambda).Skip<T>(pageSize * (pageIndex - 1)) //越过多少条.Take<T>(pageSize).AsQueryable(); //取出多少条
            }else{temp = temp.OrderByDescending<T, S>(orderByLambda).Skip<T>(pageSize * (pageIndex - 1)) //越过多少条.Take<T>(pageSize).AsQueryable(); //取出多少条
            }return temp.AsQueryable();}}
第五步:创建用户管理仓储类继承基类。其实数据的增删改查,通过数据上下文DbContext进行处理就好了,每个表对应其下的DbSet,这样我们每个model对应的操作,直接通过继承基类就可以实现了。
public class UserRepository : BaseRepository<UserProfile>{public override UserInfo Find(int id){return dbContext.UserProfiles.SingleOrDefault(u => u.UserId== id);}}
 
 
  public class ExtraUserInfoRepository:BaseRepository<ExtraUserInfo>{public override ExtraUserInfo Find(int id){return dbContext.ExtraUserInfos.SingleOrDefault(u => u.ID == id);}}
创建完成后就是以下目录结构:

 

第六步:修改注册方法,改为如下:(注:其实UserRepository和ExtraUserInfo可以抽调为全局变量,供其它方法调用,在此为了演示写在方法内部)

 [HttpPost][AllowAnonymous][ValidateAntiForgeryToken]public ActionResult Register(RegisterModel model){if (ModelState.IsValid){// 尝试注册用户try{UserRepository userRsy = new UserRepository();UserInfo userModel = userRsy.Find(model.UserId);if (userModel != null){ModelState.AddModelError("", "当前用户已存在,请重新选择");return View(model);}                   ExtraUserInfo extraUserModel = new ExtraUserInfo{UserId = model.UserId,GroupId = model.GroupId,Gender = model.Gender,Email = model.Email,SecurityQuestion = model.SecurityQuestion,SecurityAnswer = model.SecurityAnswer,RegTime = model.RegTime,LastLoginTime = model.LastLoginTime};ExtraUserInfoRepository extraUserRsy = new ExtraUserInfoRepository();if (extraUserRsy.Add(extraUserModel)){WebSecurity.CreateUserAndAccount(model.UserName, model.Password);WebSecurity.Login(model.UserName, model.Password);return RedirectToAction("Index", "Home");}else{ModelState.AddModelError("", "在用户注册时,发生了未知错误");}}catch (MembershipCreateUserException e){ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));}}// 如果我们进行到这一步时某个地方出错,则重新显示表单return View(model);}

第七步:最后就是修改注册页面:Views\Account\Register.cshtml,把扩展信息内容添加上去

 <li>@Html.LabelFor(model => model.Email)@Html.EditorFor(model => model.Email)</li><li>@Html.LabelFor(m => m.SecurityQuestion)@Html.EditorFor(m => m.SecurityQuestion)</li><li>@Html.LabelFor(m => m.SecurityAnswer)@Html.EditorFor(m => m.SecurityAnswer)</li>

最后,编译运行,可以成功注册,同时数据库多了表dbo.ExtraUserInfo,数据也有(注:为了符合个人习惯,我后面是把UserProfile改为了UserInfo,其字段UserId改为ID)

至此基本完成了注册,登录,管理功能,当然这是简单的,实际项目还有细节要处理。再留意上图,dbo.webpages_Roles和dbo.webpages_UserInRoles还没有派上用途呢,下一篇将讲解怎么利用它实行权限管理。