ASP.NET MVC4 菜鸟项目之路(一)改造示例代码扩展用户信息管理_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > ASP.NET MVC4 菜鸟项目之路(一)改造示例代码扩展用户信息管理

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

 2013/9/7 21:12:34  langyue_wu  博客园  我要评论(0)
  • 摘要:PS:本文不是大牛的经验之谈,而确确实实是菜鸟的学习归纳,虽说也有几年开发经验,但对MVC也是只闻其声,不见其人,最近闲下来也就开始接触它。学东西以项目驱动效率最高,没项目的话亲手敲下代码做下demo也行,在网上搜了一轮,都是理论一堆,实例较少,或者实例本来简单偏却复杂化,是的,我力求东西简单,同时做为基础教程,也不打算引入JS框架,权作抛砖引石之用,真要用的话视乎项目调整就是了。因为是菜鸟,难免写得有不足之处,希望大牛路过给点建议,新人路过给点鼓励。感谢:本文引用了张善友、老A、网魂小兵
  • 标签:.net ASP.NET MVC net 代码 项目 用户

PS:本文不是大牛的经验之谈,而确确实实是菜鸟的学习归纳,虽说也有几年开发经验,但对MVC也是只闻其声,不见其人,最近闲下来也就开始接触它。学东西以项目驱动效率最高,没项目的话亲手敲下代码做下demo也行,在网上搜了一轮,都是理论一堆,实例较少,或者实例本来简单偏却复杂化,是的,我力求东西简单,同时做为基础教程,也不打算引入JS框架,权作抛砖引石之用,真要用的话视乎项目调整就是了。因为是菜鸟,难免写得有不足之处,希望大牛路过给点建议,新人路过给点鼓励。

感谢:本文引用了张善友、老A、网魂小兵、洞庭夕照、蒋叶湖、ppy2790等人文章内容

模板创建示例项目

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

      打开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连接字符串即可指向我们自己的数据库(本来我想换成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, 增加一个名为ExtraUserInformation的新类。这个类代表了将在数据库创建的新表。

[Table("ExtraUserInformation")]
    public class ExternalUserInformation
    {
        [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 : ExternalUserInformation

 第二步:在UsersContext类里,增加ExternalUser,为新类创建一个DbSet属性,如下所示。

 public class UsersContext : DbContext
    {
        public UsersContext()
            : base("DefaultConnection")
        {
        }

        public DbSet<UserProfile> UserProfiles { get; set; }
        public DbSet<ExternalUserInformation> ExternalUsers { get; set; }
    }
第三步:创建【Domain】文件夹,其下创建【Repository】文件夹,文件夹下创建一个仓储接口 
public interface IRepository<TModel> where TModel : class
    {
        /// <summary>
        /// 添加【继承类重写后才能正常使用/// </summary>
        bool Add(TModel Tmodel);
        /// <summary>
        /// 更新【继承类重写后才能正常使用】
        /// </summary>
        bool Update(TModel Tmodel);
        /// <summary>
        /// 删除【继承类重写后才能正常使用】
        /// </summary>
        bool Delete(int Id);

        /// <summary>
        /// 查找指定值【继承类重写后才能正常使用】
        /// </summary>
        TModel Find(int Id);
    }

第四步:创建仓储基类,实现接口

public abstract class RepositoryBase<TModel> : IRepository<TModel> where TModel : class
    {
        public UsersContext dbContext;
        public RepositoryBase()
        {
            dbContext = new UsersContext();
        }

        /// <summary>
        /// 添加【继承类重写后才能正常使用】
        /// </summary>
        public virtual bool Add(TModel Tmodel) { return false; }
        /// <summary>
        /// 更新【继承类重写后才能正常使用】
        /// </summary>
        public virtual bool Update(TModel Tmodel) { return false; }
        /// <summary>
        /// 删除【继承类重写后才能正常使用】
        /// </summary>
        public virtual bool Delete(int Id) { return false; }

        /// <summary>
        /// 查找指定值【继承类重写后才能正常使用】
        /// </summary>
        public virtual TModel Find(int Id) { return default(TModel); }


        ~RepositoryBase()
        {
            if (dbContext != null)
            {
                dbContext.Dispose();
            }
        }
    }

第五步:创建用户管理仓储类继承基类。其实数据的增删改查,通过数据上下文DbContext进行处理就好了,每个表对应其下的DbSet,关于它,可以去看看MvcMovie。

public class UserRepository : RepositoryBase<RegisterModel>
    {
        /// <summary>
        /// 添加用户
        /// </summary>
        /// <param name="user">用户信息</param>
        /// <returns></returns>
        public override bool Add(RegisterModel user)
        {
            bool isSuccess = false;
            if (user != null)
            {
                //dbContext.UserProfiles.Add(new UserProfile { UserName = user.UserName });
                //dbContext.SaveChanges();
                ExternalUserInformation newExtUser = new ExternalUserInformation
                {
                    UserId = user.UserId,
                    GroupId = user.GroupId,
                    Email = user.Email,
                    SecurityQuestion = user.SecurityQuestion,
                    SecurityAnswer = user.SecurityAnswer,
                    RegTime = user.RegTime,
                    LastLoginTime = user.LastLoginTime
                };
                dbContext.ExternalUsers.Add(newExtUser);
                isSuccess = dbContext.SaveChanges() > 0;
            }
            return isSuccess;
        }
        /// <summary>
        /// 更新用户信息
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public override bool Update(RegisterModel user)
        {
            var userObj = dbContext.ExternalUsers.SingleOrDefault(u => u.UserId == user.UserId);
            if (userObj == null) return false;
            userObj = user;
            if (dbContext.SaveChanges() > 0) return true;
            else return false;
        }
    }

创建完成后就是以下目录结构

 

第六步:修改注册方法,改为如下:

     [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // 尝试注册用户
                try
                {
                     UserRepository userRsy = new UserRepository();
                     if (userRsy.Add(model))
                     {
                         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.ExtraUserInformation,

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

发表评论
用户名: 匿名