背景
最近使用asp.mvc 做一个在线口语系统项目,在服务端验证问题遇到了一些小问题。
自己根据数据库表user定义一个数据库表实体对象UserDbEntity
1 [Table("User")] 2 public class UserDbEntity : DbEntityModelBase 3 { 4 [Description("用户名")] 5 [Required(ErrorMessage="*")] 6 public string Name 7 { 8 get; 9 set; 10 } 11 [Description("邮箱")] 12 [Required] 13 public string Email 14 { 15 get; 16 set; 17 } 18 [Description("密码")] 19 [Required(ErrorMessage="*")] 20 public string Pwd 21 { 22 get; 23 set; 24 } 25 [Description("确认密码,数据库不存在该字段")] 26 [Required(ErrorMessage = "*")] 27 public string SecondPwd 28 { 29 get; 30 set; 31 } 32 33 [Description("真实姓名")] 34 [Required] 35 public string TrueName 36 { 37 get; 38 set; 39 } 40 41 [Description("邮箱是否已激活,长度为1")] 42 public int Actived 43 { 44 get; 45 set; 46 } 47 } 48 49 /// <summary> 50 /// 所有DbEntityModel项目中的实体必须继承DbEntityModelBase或其子类,使用supperType模式控制共有子类的行为或者状态,此项目中的类根据数据库基本表或者视图保持基本一致 51 /// </summary> 52 public abstract class DbEntityModelBase 53 { 54 [Description("Guid标识")] 55 public string GuidMark 56 { 57 get; 58 set; 59 } 60 [Description("自增Id列")] 61 public int Id 62 { 63 get; 64 set; 65 } 66 [Description("排序,倒序")] 67 public int Sort 68 { 69 get; 70 set; 71 } 72 }
在前端页面有一个登陆页面直接使用UserDbEntity实体对象UserDbEntity
1 <form id="login_form" style="padding: 10px 20px 10px 40px;" action="Login" method="post"> 2 <p> 3 <em>用户名</em>@Html.EditorFor(model=>model.User.Name) @Html.ValidationMessageFor(model=>model.User.Name)</p> 4 <p> 5 <em>密码</em>@Html.PasswordFor(model=>model.User.Pwd) @Html.ValidationMessageFor(model=>model.User.Pwd)</p> 6 <div style="padding: 5px; text-align: center;"> 7 <a href="#" onclick="$(‘#login_form‘).submit();return false" class="easyui-linkbutton" icon="icon-ok">登录</a> 8 <a href="#" onclick="$(‘#login_form‘)[0].reset()" class="easyui-linkbutton" 9 icon="icon-cancel">重填</a> 10 </div> 11 </form>
在控制器中对输入进行验证
1 [HttpPost] 2 public ActionResult Login(UserDbEntity loginModel) 3 { 4 bool loginFlag = false; 5 string name = loginModel.Name; 6 string pwd = loginModel.Pwd; ; 7 if(ModelState.IsValid){ 8 return Login(); 9 } 10 var user = new UserDbEntity(); 11 12 using (var scope = IocRegisterBLL.ContainerBLLComponent.BeginLifetimeScope()) 13 { 14 var userBLL = scope.Resolve<UserBLL>(); 15 loginFlag = userBLL.AdminLogin(name, pwd, out user); 16 } 17 18 if (loginFlag) 19 { 20 Session[C_LogOnSession] = user; 21 return Index(); 22 } 23 else 24 { 25 return Login(); 26 } 27 }
ModelState.IsValid 永远是false。因为我们没有对email是空,用mvc服务端验证机制验证,永远不能通过。
遇到问题,我们就要思考。现在我有两种思路解决问题。
第1种方法是。 为每个页面制定一个ViewModel, 然后这个ViewModel在使用DataAnnotations ,最后使用mvc服务端的验证机制。 当然这个方法也是大项目使用最多的方法。 ViewModel 和UserDbEntity之间的转化时候AutoMapper 进行DTO。 但这个方法我不太喜欢,因为我觉得我这个项目属于中小项目,没必要每个页面制定一个ViewModel。 于是我找到了第2种方法。
第2种方法。使用FluentValidation验证。使用FluentValidation 为login页面 对 UserDbEntity 写一个验证类。
使用FluentValidation 解决背景介绍中遇到的问题
1 引用: FluentValidation.dll ,可以直接 Nuget ,Install-Package FluentValidation。
2. 编写UserLoginValidator类
1 public class UserLoginValidator: AbstractValidator<UserDbEntity> 2 { 3 public UserLoginValidator() 4 { 5 RuleFor(u=>u.Name).NotNull().WithMessage("用户名不能为空"); 6 RuleFor(u => u.Pwd).NotNull().WithMessage("密码不能为空"); 7 } 8 }
3、在login controller使用 UserLoginValidator类进行验证。
1 [HttpPost] 2 public ActionResult Login(UserDbEntity loginModel) 3 { 4 bool loginFlag = false; 5 string name = loginModel.Name; 6 string pwd = loginModel.Pwd; 7 UserLoginValidator validInstance = new UserLoginValidator(); 8 if(!validInstance.Validate(loginModel).IsValid){ 9 return Login(); 10 } 11 var user = new UserDbEntity(); 12 13 using (var scope = IocRegisterBLL.ContainerBLLComponent.BeginLifetimeScope()) 14 { 15 var userBLL = scope.Resolve<UserBLL>(); 16 loginFlag = userBLL.AdminLogin(name, pwd, out user); 17 } 18 19 if (loginFlag) 20 { 21 Session[C_LogOnSession] = user; 22 return Index(); 23 } 24 else 25 { 26 return Login(); 27 } 28 }
这样就能解决我在背景中碰到的问题。 如果项目中其他页面也用到到UserDbEntity,但验证规则需要个性化,你也可以另外针对UserDbEntity写另外一个Validator类。
这样就不必要对给个页面制定一个ViewModel了。
推荐在项目中使用FluentValidation进行服务端验证
FluentValidation 在个github上的地址 https://github.com/JeremySkinner/FluentValidation。FluentValidation的链式方法调用,写验证非常的流畅。可以为你在项目中写服务端验证逻辑节省不少时间。