.Net高级进阶,教你如何构建企业模型数据拦截层,动态控制字段验证

如今,你有一个MVC架构的web项目,你要完成一个注册功能。html

前台传了3个值到你的控制器,分别是帐号、密码、邮箱。web

如图:如今你要在控制器里面判断,帐号名称、密码、邮箱不能为空,而且名称和密码不超过16位。正则表达式

上面这个图只是个理想中的小例子,实际开发状况是,可能一次性要传十几个字段甚至更多。c#

那么在实际开发中,一般为了复用性,咱们将这3个参数用一个实体类来代替。设计模式

即以下所示。api

注:这一步会有个知识点,叫作模型验证,不懂的童鞋能够百度下,MVC会经过必定规则自动直接将参数反序列化成所对应的实体类,可是由于我这个示例是webapi模式的,写法略有不一样,因此还要在参数前加个[FromBody]才能自动反序列化。架构

至于具体为何会自动反序列化,在本篇并非我要讲的主题,因此感兴趣的童鞋能够百度下:MVC下的ModelBinder    。mvc

拦截层的解耦

如今,我认为把实体类验证给带到控制器里去写的这种方式有点不美,若是业务规则多的话,那么这样的验证代码就很是庞大,而且若是整个项目都采用这种验证模式,那么在我往后的维护阶段中就显得有点臃肿的感受,实体类依赖于控制器方法去验证,我得先找到这个实体类,而后仔细想一想有哪些方法用到了该实体类,又作了哪些验证判断,而后维护。框架

那么我能不能在控制器方法中 验证明体类这一步 给挪掉,不写到控制器的方法当中,写在另外一个地方,统一进行管理,实现实体类的验证与控制器中的方法业务逻辑分除。ide

这种行为操做有点像httpModule,思想上就是设计模式所谓的下降耦合性了。

那么怎么作呢?

咱们能够直接在实体类中加验证,如图

上面看到[Required],[StringLength],[RegularExpression]的这些叫作验证特性,是.net框架已经封装好的,它会对标注特性的字段采起验证。

[Required]限制了必须输入,[Required(ErrorMessage = "请输入用户名")]  

[StringLength]限制了规定的长度,[StringLength(10, ErrorMessage = "长度不能超过10个字符")]  

[Range] 限制了值的范围,[Range(0, 120, ErrorMessage = "年龄范围在0到120岁之间")]  

[RegularExpression] 限制了必须知足正则表达式,[RegularExpression(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$", ErrorMessage = "请输入Email格式")]  

[Compare]限制了与之对应的字段相等,[Compare("pwd", ErrorMessage = "两次密码要一致")]  //该特性标注的字段值必须与pwd字段值相等

.net也就封装了几个,这5个用的最多(固然,也能够自定义这种验证特性,对这块想深刻了解的请百度:mvc ValidationAttribute)。

那么我标注了特性后如何进行判断呢?

  

咱们看下控制器方法中的写法:

如图,用 ModelState.IsValid  这段话来对验证结果进行断定,若是实体类上的被标注的特性知足条件的话,就为true,不然为false。

那么,由于这种模型验证是种模式,是全局的,因此应该单独拿出来在拦截层进行注册。

如图:

这段代码的意思就是:每当进入控制器方法以前,会判断这个方法的名称,若是包含的有Insert、check、update这三者的任意一个,都会进行拦截验证(对模型验证的结果进行断定),若是为false,那么就返回给客户端一个400状态码。

而后注册一下:(注册的地方只是个范例,由于我是webapi,只对http进行拦截)

 

model负责填写规则,验证由专门的验证人员去作,逻辑由专门的逻辑人员去写,这样就各司其职了。

 不过,这才只是第一步!

(随着你平常的开发,你确定会遇到这种状况)

user实体类,是专一于注册方法,说白了,就是为注册方法所写的,

我如今还要写个登陆方法。

可是登陆的时候,我不须要填写email,只须要填写帐号和密码,对这两个字段进行验证。

但是个人实体类里面对email作了[Required]和[RegularExpression]验证,那么这样就致使了 若是我登陆方法继续使用这个user实体类,那么确定会报错,会返回个400验证码。

这种状况我该怎么解决?难道从新建个model?再从新给一遍规则?这还仅仅只有3个字段,万一有的表中有十几个字段,二十几个字段甚至更多怎么办?

从新建个model确定不行,这样已经失去了   复用性、各司其职  的初衷。

求解决方案!在线等!

...

模型验证进阶:自由控制须要验证的字段

百度了一下,网上没有该方面的教程,博客园中也没找到,群里也没交流出个结果,但这种状况却常常遇到!

 梳理下思路,大体有几种,第一种是用某种手段控制类中的这些验证特性,或者控制类中的属性字段,如启用或停用,可是c#不能对属性字段进行停启用,而控制类中的这些验证特性也有点天方夜谭,自己就是微软封装好的,你得反编译一下看下源码,而后重构?或者你直接不用这些框架封装好的验证特性,使用本身定义自定义验证特性,而后把控制方法都写在里面?这样太麻烦,并且违背初衷。自定义ModelBinder ?更扯淡。

一番折腾无果,那么就不能从特性自己找突破口了,这时,我把目标转移到ModelState.IsValid上,换一种思路实现。

咱们发现其实现了GetEnumerator方法,因而对其进行遍历,能够获取到特性所绑定的字段属性的名称以及其状态。

由于要实现自由控制须要验证的字段,因此不管怎样实现,都只能经过 自定义特性 标注在方法体头上来实现。

而理想的最终呈现效果应该是这样的:

放图:

 

或者

 

 

使用方式:

若是方法头上有KeepZ特性的话,就进入自由控制验证字段状态。

 

[KeepZ("字段1","字段2")]  即:只对  字段1 和 字段2   进行验证

[KeepZ(false,"字段3")]  即:除了  字段3  以外,其他字段都进行验证

 

 

那么咱们放下具体实现代码:

 

 public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if ((actionContext.ActionDescriptor.ActionName.ToUpper().Contains("INSERT") || actionContext.ActionDescriptor.ActionName.ToUpper().Contains("CHECK") ||
                 actionContext.ActionDescriptor.ActionName.ToUpper().Contains("UPDATE")))
            {
                var ia = actionContext.ActionDescriptor.GetCustomAttributes<KeepZ>();
                if (ia.Count != 1)
                {
                    goto result;
                }
                foreach (KeyValuePair<string, ModelState> item in actionContext.ModelState.ToArray())
                {
                    if (ia[0].Modes == false)
                    {
                        foreach (string PropertysValue in ia[0].Propertys)
                        {
                            if (item.Key.Contains(PropertysValue))
                            {
                                actionContext.ModelState.Remove(item.Key);
                            }
                        }
                    }
                    else
                    {
                        bool re = false;
                        foreach (string PropertysValue in ia[0].Propertys)
                        {
                            if (item.Key.Contains(PropertysValue))
                            {
                                re = true;
                            }
                        }
                        if (re == false)
                        {
                            actionContext.ModelState.Remove(item.Key);
                        }
                    }
                }
                result: if (!actionContext.ModelState.IsValid)
                {
                  
                    actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
                   
                      
                }
            }
        }
/// <summary>
    ///  保持
    /// </summary>
    public class KeepZ : Attribute
    {
        public string[] Propertys = null;
        public bool Modes = true;
        public KeepZ(params string[] Property)
        {
            Propertys = Property;
        }
        public KeepZ(bool Mode, params string[] Property)
        {
            Propertys = Property;
            Modes = Mode;
        }
    }

如此一来,就不用再重建Model这样费时费力的方法了,如今MVC架构大多都用这种验证模式,可是却没有  自由选择验证字段的解决方案,往往遇到该状况,只能无奈从新建个实体类,对比之下,根本没有食得这种拦截层模型验证的精髓,只学个模子,反而弄巧成拙不成本意,因此我写了此篇和你们一块儿分享,加入了KeepZ来控制须要验证的字段,就是真正的实现了  可 复用  ,逻辑与拦截分层  了。

Demo虽小,可是这种状况下的解决方案,我在博客园中没找到,应该是园子里第一篇吧。

注意,BindAttribute 这个特性,是 针对赋值上的处理, 选择赋值 和 选择验证 是 两个 看似相同却大相径庭的两个分支,本文是 自由控制验证拦截 ,所适用性在某些场景要比 BindAttribute 要多,固然,不介意的话, 你也能够 将 BindAttribute  带入本实例中,也是能够的, 剔除验证的时候 剔除赋值。

 

做者:小曾
出处:http://www.cnblogs.com/1996V/p/7423834.html 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文连接。如您有任何疑问或者受权方面的协商,请给我留言
.Net交流群, QQ群:166843154 欲望与挣扎 
相关文章
相关标签/搜索