码迷,mamicode.com
首页 > Web开发 > 详细

.NET MVC 4 实现用户注册功能

时间:2015-12-29 00:57:14      阅读:387      评论:0      收藏:0      [点我收藏+]

标签:

初学MVC,踩了不少坑,所以通过实现一个用户注册功能把近段时间学习到的知识梳理一遍,方便以后改进和查阅。

 

问题清单:

l 为什么EF自动生成的表名后自动添加了s

l 如何为数据库初始化一些数据?

l 使用WebAPI如何返回JSON

l 让Action接受Get请求

l 如何使路由匹配不同的URL

l 如何调试路由

l VS2013如何添加jQuery智能提示?

l 为何在Session中的验证码打印出来后与上一次的相同?

l 对一个或多个实体的验证失败(db.SaveChanges不起作用)

l 数据库正在使用,无法删除

 

数据库设计(Code First)

这里并没有采用传统的数据库设计方案,而是使用了 代码优先(code first)这种模式适用于开发初期,数据库设计目标还不明确的阶段,可以随时修改表和字段。打开VS,新建一个项目,选择ASP>NET MVC 4 Web应用程序:

 技术分享

操作完成后,可以看到以下目录结构:

技术分享

 

选择Models文件夹,新建一个类Model.cs

 

技术分享
 1 namespace xCodeMVC.Models
 2 {
 3     public class UserInfo
 4    {
 5         //ID
 6         public int UserID { get; set; }
 7 
 8         //用户名
 9         public string UserName { get; set; }
10 
11         //密码
12         public string UserPwd { get; set; }
13 
14         //邮箱
15         public string UserEmail { get; set; }
16 
17         //用户组:0代表管理员,1代表普通用户
18         public int UserRank { get; set; }
19 
20         //注册时间
21         public DateTime RegisterTime { get; set; }
22     }
23 }
View Code

 

 

初步设计已经完成了,下面需要对各个字段进行约束:

l UserID:主键、自增长

l UserName:长度为215个字符、必填

l UserPwd:长度为620个字符、必填

l UserEmail:必填

l UserRank:默认为1

l RegisterTime:注册时间(DateTime格式)

 

添加约束后的代码:

技术分享
 1 namespace xCodeMVC.Models
 2 {
 3     public class UserInfo
 4     {
 5         [Key]
 6         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
 7         public int UserID { get; set; }
 8 
 9         [Required(ErrorMessage="用户名不能为空")]
10         [Display(Name="用户名")]
11         [StringLength(20,MinimumLength=2,ErrorMessage="用户名必须为{2}到{1}个字符")]
12         public string UserName { get; set; }
13 
14         [Required(ErrorMessage="密码不能为空")]
15         [Display(Name="密码")]
16         [StringLength(50, MinimumLength = 6, ErrorMessage = "密码必须为{2}到20个字符")]
17         [DataType(DataType.Password)]
18         public string UserPwd { get; set; }
19 
20 
21         [Display(Name="邮箱")]
22         [Required(ErrorMessage="邮箱必填")]
23 [RegularExpression(@"^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$",
24  ErrorMessage = "请输入正确的Email格式\n示例:abc@123.com")]
25         public string UserEmail { get; set; }
26 
27         public int UserRank { get; set; }
28 
29         public DateTime RegisterTime { get; set; }
30     }
31 }
View Code

至此,一个model就建好了。

接着需要配置一下web.config,在configuration节点内添加数据库连接字符串,后面实体会用到:

 

技术分享
1 <configuration>
2   <connectionStrings>
3     <add name="connection" providerName="System.Data.SqlClient" connectionString="Data Source=.;Initial Catalog=cxyDB;Integrated Security=True" />
4   </connectionStrings>
5 </configuration>
View Code

 

Models文件夹内再建一个新类DBContext.cs,用于进行数据库的相关操作:

 

技术分享
 1 namespace xCodeMVC.Models
 2 {
 3     public class DBContext : DbContext
 4    {
 5     //connection是webconfig内的连接字符串
 6         public DBContext() : base("connection") { }
 7 
 8         public DbSet<UserInfo> userInfo { get; set; }
 9     }
10 }
View Code

 

最后需要在Global.asax文件中添加如下配置(如何为数据库初始化一些数据?)

技术分享
 1 using xCodeMVC.Models;
 2 
 3 public class MvcApplication : System.Web.HttpApplication
 4 {
 5 //DropCreateDatabaseIfModelChanges表示当模型改变时删除并重新创建数据库
 6 //还有一个Always表示总是在启动时执行删除并重建数据库操作
 7     public class DBInit:DropCreateDatabaseIfModelChanges<DBContext>
 8         {
 9             protected override void Seed(DBContext context)
10             {
11                 //为数据库insert一些初始数据
12                 context.userInfo.Add(new UserInfo
13                 {
14                     UserName = "troy",
15                     UserPwd = "111111",
16                     UserEmail = "abc@163.com",
17                     UserRank = 0,
18                     RegisterTime = DateTime.Now
19                 });
20                 base.Seed(context);
21             }
22         }
23     protected void Application_Start()
24     {
25         Database.SetInitializer(new DBInit());
26         //省略生成时的代码...
27     }
28 }
View Code

启动项目,会发现程序自动生成了cxyDB的数据库,并添加了一个名为UserInfoes的表,里面有一条初始记录:技术分享

 

不过需要注意,这里生成的表名是UserInfoes,后面会说明这种情况(为什么EF自动生成的表名后自动添加了s)。

 

表单设计

 技术分享

l 客户端验证

技术分享

 

首先焦点移出文本框时,需要远程访问一个API,查询数据库中用户名是否存在。在Controllers文件夹选中AccountController.cs控制器并添加如下代码:

技术分享
 1 namespace xCodeMVC.Controllers
 2 {
 3     public class AccountController : Controller
 4     {
 5         private DBContext db = new DBContext();
 6         // GET: /Account/CheckUser
 7         [HttpGet]
 8         public JsonResult CheckUser(string username)
 9         {
10             var exists = db.userInfo.Where(a => a.UserName == username).Count() != 0;
11 
12             return Json(exists, JsonRequestBehavior.AllowGet);
13         }
14     }
15 }
View Code

客户端用如下代码发起请求:

技术分享
1 $.getJSON("/Account/CheckUser/?username=" + username, function (data) {
2 if(data) {
3    //用户名存在
4 }
5 });
View Code

 

l 图形验证码

在解决方案中新建一个类库项目,编写生成图形验证码的代码,编译后在MVC项目中引用其生成的dll文件

 

技术分享
 1 public ActionResult GetValidateImg()
 2 {
 3      int width = 60, height = 28, fontsize = 12;
 4      string code = string.Empty;
 5      byte[] bytes = ValidateCode.CreateCode(out code, 4, width, height, fontsize);
 6 
 7      Session["v_code"] = code.ToLower();
 8 
 9      return File(bytes,@"image/jpeg");
10 }
View Code

 

视图

这里没有使用原生的form表单,而是使用了MVChtml辅助方法。

首先要在页面中引入所需的model

 

@model xCodeMVC.Models.UserInfo

 

这样就能使用表单增强工具了(省略了一些代码):

技术分享
 1 @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { name = "register",onsubmit = "return checkform()"}))
 2 {
 3 @Html.LabelFor(model => model.UserName)
 4 @Html.TextBoxFor(model => model.UserName, new { @class = "text-box" })
 5 
 6 @Html.LabelFor(model => model.UserPwd)
 7 @Html.EditorFor(model => model.UserPwd)
 8 
 9 @Html.LabelFor(model => model.UserEmail)
10 @Html.EditorFor(model => model.UserEmail)
11 
12     <input class="regBtn" type="submit" value="注册" />
13 <input class="resetBtn" type="reset" value="重置" />
14 
15 //令牌,防止重复提交
16 @Html.AntiForgeryToken()
17 //模型错误信息汇总,也可以在每一项后面添加
18 //@Html.ValidationMessage
19 @Html.ValidationSummary(false)
20 }
View Code

不使用原生form是为了精简代码,将复杂的验证逻辑交给MVC框架去做。

 

 

完成后台注册

表单提交的地址是AccountController中的Register方法,该方法只接受HttpPost请求。

 

技术分享
 1 // POST: /Account/Register
 2 [HttpPost]
 3 [AllowAnonymous]
 4 [ValidateAntiForgeryToken]
 5         
 6 public ActionResult Register(UserInfo userInfo)
 7 {
 8     string checkPwd = Request["ChkUserPwd"].ToString();
 9     string vCode = Request["vCode"].ToString().ToLower();
10 
11     if(string.IsNullOrEmpty(checkPwd))
12     {
13         ModelState.AddModelError("ChkUserPwd", "确认密码不能为空");
14     }
15     else
16 {
17     if (Md5Hash(checkPwd) != Md5Hash(userInfo.UserPwd))
18         {
19               ModelState.AddModelError("PwdRepeatError", "确认密码不正确");
20         }
21     }
22             
23 
24     if (!ChkValidateCode(vCode))
25     {
26          ModelState.AddModelError("vCode", "验证码不正确");
27     }
28 
29     bool isUserExists = db.userInfo.Where(a => a.UserName == userInfo.UserName).Count() != 0;
30     bool isEmailExists = db.userInfo.Where(a => a.UserEmail == userInfo.UserEmail).Count() != 0;
31 
32     if (isUserExists) ModelState.AddModelError("UserName", "用户名已被占用");
33     if (isEmailExists) ModelState.AddModelError("UserEmail", "邮箱已被注册");
34 
35             
36     if(!ModelState.IsValid)
37     {
38         return View(userInfo);
39     }
40     userInfo.RegisterTime = DateTime.Now;
41     userInfo.UserPwd = Md5Hash(userInfo.UserPwd);
42     try
43     {
44         db.userInfo.Add(userInfo);        
45         db.SaveChanges();
46         return RedirectToAction("Index", "Home");
47     }
48     catch (DbEntityValidationException dbEx)
49     {
50         foreach (var validationErrors in dbEx.EntityValidationErrors)
51         {
52             foreach (var validationError in validationErrors.ValidationErrors)
53             {
54             System.Diagnostics.Trace.TraceInformation("Property: {0} Error: {1}",
55             validationError.PropertyName,
56             validationError.ErrorMessage);
57             }
58         }
59         throw;
60     }
61 }
View Code

 

 

 

问题汇总

l 为什么EF自动生成的表名后自动添加了s

 

这种情况是EF默认的,可以修改一些配置去掉默认规则。

方法一:

Models.cs中修改,在类名前加上属性[Table(TableName)]

 

 

技术分享
1 namespace xCodeMVC.Models
2 {
3     [Table("UserInfo")]
4     public class UserInfo
5     {
6         public int UserID { get; set; }
7         //......
8     }
9 }
View Code

方法二:

DBContext.cs中修改

 

技术分享
 1 namespace xCodeMVC.Models
 2 {
 3     public class DBContext : DbContext
 4 {
 5         protected override void OnModelCreating(DbModelBuilder modelBuilder)
 6         {
 7             //modelBuilder.Entity<UserInfo>().ToTable("UserInfo");
 8 //或者
 9 //移除默认约定规则,比如在表名后默认加上“s”
10 modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
11             base.OnModelCreating(modelBuilder);
12     }
13 
14         public DBContext() : base("connection") { }
15 
16     public DbSet<UserInfo> userInfo { get; set; }
17 }
18 }
View Code

 

 

l 如何为数据库初始化一些数据?

l 使用WebAPI如何返回JSON

 

打开AppStart中的webapi配置文件

技术分享

将以下代码添加到Register中:

 

技术分享
1 //webapi默认返回xml格式,添加如下代码将返回json格式
2 config.Formatters.JsonFormatter.SupportedMediaTypes.Add(
3                 new MediaTypeHeaderValue("text/html"));
View Code

 

webapiController中使用object返回json,例如:

 

技术分享
1 public object GetUserInfoByName(string username)
2 {
3     username = HttpUtility.UrlDecode(username);
4     return GetUserInfo(a=>a.UserName == username);
5 }
View Code

 

 

l 让Action接受Get请求

 

在方法名前添加属性或者为方法名添加Get前缀

技术分享
1 [System.Web.Http.HttpGet]
2 public bool GetUserExists(string username)
View Code

 

l 如何使路由匹配不同的URL

 

可以参考下面的匹配模式,重点在于为每个路由指定相应的actionurl里可以没有actioncontroller,但为其指定一些值有助于区分各个路由。

 

技术分享
 1 //api/getuser/1
 2 config.Routes.MapHttpRoute(
 3       name: "getUserInfoByID",
 4       routeTemplate: "api/{controller}/{id}",
 5       constraints: new { id = @"^\d*$" },
 6       defaults: new { controller = "getuser", id = RouteParameter.Optional }
 7 );
 8 
 9 //api/getuser/troy
10 config.Routes.MapHttpRoute(
11        name: "getUserInfoByName",
12        routeTemplate: "api/{controller}/{username}",
13        constraints: new { username = @"^\w*$" },
14        defaults: new { controller = "getuser", action = "GetUserInfoByName" }
15 );
16 
17 //访问形式 api/getuser/?ids=1,3,52,100...
18 config.Routes.MapHttpRoute(
19       name: "getUserInfoByCoupleOfIds",
20       routeTemplate: "api/{controller}/ids={ids}",
21       constraints: new { ids = @"^\d+,?$" },
22       defaults: new { controller = "getuser" }
23 );
24 
25 //api/getuser/check=troy
26 config.Routes.MapHttpRoute(
27       name: "ChkUserExists",
28       routeTemplate: "api/{controller}/check={username}",
29       constraints: new { username = @"\w*" },
30       defaults: new { controller = "getuser", action = "ChkUserExists" }
31 );
View Code

 

 

l 如何调试路由

很多时候不知道程序采用了哪个路由,可以安装RouteDebugger来查看当前匹配了哪个路由。

安装方法:

工具->NuGet程序包管理器->控制台->Install-Package RouteDebugger

等待安装完成,在web.configappsettings节点下可以看到

 

<add key="RouteDebugger:Enabled" value="true" />

 

表示路由调试已经打开了,运行程序就可以看到。

 

l VS2013如何添加jQuery智能提示?

 

在脚本中添加如下代码:

/// <reference path="jquery-1.11.1.js" />

l 为何在Session中的验证码打印出来后与上一次的相同?

 

这其实是正确的,因为页面生成在前,而访问验证码在后,Session是在生成验证码时记录的,此时页面的Session还是空的,随后它的值才被赋为验证码的值,所以刷新页面就看到了上一次Session中的验证码。

 

 

 

客户端通过以下代码访问验证码:

技术分享
1 <img id="v_code" class="imgborder"                        src="@Url.Action("GetValidateImg", "Account")?t=@DateTime.Now.Ticks"
2 alt="看不清,点击换一张" />
View Code

l 对一个或多个实体的验证失败(db.SaveChanges不起作用)

 

检查模型的约束要求与数据库设计是否一致,字符串长度超限等等这样的错误是不能保存成功的,但往往VS调试时又不能给出具体的错误在哪,所以可以添加一些代码查看错误详细信息。这样就能在输出窗口中可以看到具体的错误。

 

l 数据库正在使用,无法删除

当模型改动时,之前在Global中的设置会删除并重建数据库,但如果此时你对这个数据库有操作,比如查询之类的,删除就会失败,提示你数据库在使用。这个没找到好的解决方法,我只好采取关掉SQL Server服务再重启这样的笨方法来解决。

 

.NET MVC 4 实现用户注册功能

标签:

原文地址:http://www.cnblogs.com/undefined000/p/5084365.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!