在web开发中必不可少的会遇到表单验证的问题,为避免数据在写入到数据库时出现异常,通常比较安全的作法是前端会先作一次验证,经过后把数据提交到后端再验证一次,由于仅仅靠前端验证是不安全的,有太多的http请求工具能够轻松绕过你的前端验证把危险数据提交到后端,因此,以前不作后端参数验证的同窗赶快检查一下你的代码~别中招了css
那么,故事就是有关于后端验证。html
这里举一个项目中真实的注册场景,帐号注册主要包含2个信息:手机号和验证码,由于我这里是用webapi的post方式从前端拿数据,因此封装成了一个MemberRegister对象。以最基础的非空验证为例,一般要写以下代码:前端
若是还要加上手机号格式验证,还得再来一个if。一旦要验证的信息多的话代码行就会不少,看着很冗余。想着既然作的都是同一件事,那能不能封装一下减小代码行?架构师allen说能够试一下链式编程,也就是相似Jquery的xxxx.attr().css().html().show()这样,看起来还不错的样子,那就干吧。web
其实C#里也有相似的用法,好比Linq里面的xxxx.Where().OrderBy().Select()这种,可是这种实际上每次返回的都是不一样的对象,而后执行对象里的方法,这并不适合个人需求,由于我执行的验证方法确定都是同一个,好比validate().validate().validate()这种,因而决定用扩展方法来实现。先定义一个被扩展的对象:数据库
public class ValidateResult<T> { public List<string> Errors { get; set; } public T Entity { get; private set; } public ValidateResult(T entity) { Errors = new List<string>(); Entity = entity; } }
定义扩展方法:编程
public static ValidateResult<T> Validate<T>(this ValidateResult<T> target, Predicate<T> predicate, string errorMessage) { if (!predicate(target.Entity)) { target.Errors.Add(errorMessage) ; } return target; }
使用办法:后端
var error = new ValidateResult<MemberRegister>(model) .Validate(m => m != null, ResponseTip.ParamError) .Validate(m => !string.IsNullOrEmpty(m.Phone), ResponseTip.PhoneRequired) .Validate(m => !string.IsNullOrEmpty(m.CodeValue), ResponseTip.ValidateCodeRequired) .Errors;
理想中的状况是,能够判断error里面有没有错误信息,若是有的话就返回错误信息,没有就作后面的操做。但实际上碰到一个问题,当model为null的时候,第一步验证没有问题,但第二步的时候就报错了,未将对象引用到实例,缘由是model已是null了再取model.Phone不出错才怪。问题找到了,那就想着若是model为null就不执行后面的验证了,想法不错但想了好久就是没找到办法实现。不知所措的时候,断点跟了一下出错的代码,发现报错的地方是在执行if (!predicate(target.Entity))的时候,因而换了一个思路,改进一下代码:api
public class ValidateResult<T> { public string Error { get; set; } public T Entity { get; private set; } public ValidateResult(T entity) { Entity = entity; } }
扩展方法:安全
public static ValidateResult<T> Validate<T>(this ValidateResult<T> target, Predicate<T> predicate, string errorMessage) { if (string.IsNullOrEmpty(target.Error)) { if (!predicate(target.Entity)) { target.Error = errorMessage; } } return target; }
改进后的代码把ValidateResult里的Errors取消了换成了string类型的Error(要那么多错误提示也没什么用,一个就够了),而后验证失败后就更新这个属性,验证的时候若是这个属性string.IsNullOrEmpty(target.Error)就表示前面的验证都经过了本次能够继续验证,若是! string.IsNullOrEmpty(target.Error)就表示前面的验证已经失败了本次不用验证,要验证的对象原封不动的返回。这样子就不会报错了,而后调用结果判断Error是否NullOrEmpty再作相应操做。测试一下,没有问题。代码演变为:架构
优势
可读性我的以为并不比直接if差,分行显示的话仍是能很清晰看出具体的验证项。
省去了每次判断的if语句和return,支持自定义验证规则和错误提示。
减小了代码的行数。
缺点
某次验证失败不能中断后面的验证,多执行了没必要要的代码,这点用if能够避免。
总结
完了之后去网上找了一些C#链式编程的问题,有支持的也有反对的,反对的人说代码可读性不太好、简单的问题复杂化等等。通过实际实践,我以为这个问题偏向于我的喜爱,谈不上好坏,怎样用着爽、开发效率高就行。不喜欢的还请轻点拍砖。
固然,关于这个问题有更好解决方案的但愿能交流一下。