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

net 专栏收录该内容
43 篇文章 0 订阅

模板创建示例项目

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还没有派上用途呢,下一篇将讲解怎么利用它实行权限管理。

  • 3
    点赞
  • 3
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值