• 项目演化系列--验证体系


    前言

      数据验证主要分成2个部分,一个是前端js对用户输入的数据进行检查,另一个是后端收到请求时对请求数据进行验证。有些web项目只在前端验证用户的输入数据,但是对于请求到后端的数据却没有进行处理,这会遗留下严重的系统漏洞,又或者开发人员分别在前后端编写验证代码,当验证规则需要调整的时候,就需要一起维护,稍有不慎就会有验证不同步的问题。对于web项目而言,有一个观念是要重视的,那就是所有请求的数据都是不安全的,因为http是没有状态的,请求都是可以模拟的。

      因此此次的文章是在前一篇文章的基础上增加验证体系,使开发人员只需要开发后端验证,通过增加相应的类库支持可以直接生成相应的组件html,而前端则需要开发组件来给予支持,虽然实现起来需要不少时间去设计、重构,但是当完成了以后获益还是很大的,不仅可以大大缩减需要开发的代码,而且可以降低维护的成本。

      之前我有写过基于EasyUI的相关扩展,可以先参考一下:

      那么废话就不多说了,直接开始吧。

    前端

      后端类库能生成的仅仅是前端组件的html,因此必须要先设计好组件,然后根据组件的验证规则得出相应的html,并根据规则来实现后端类库。

      文章中使用的是基于angular的组件,由于本人对于html实在不熟悉,因此例子比较简单,效果就只是改变文本框背景而已,正确的情况下为白色,错误时为红色,html代码如下:

    //angular组件
    <br type="text" valid-type="验证规则" data="绑定的值" uc-input />
    
    //实际html
    <br type="text" valid-type="['stringLength', { min: 6, max: 12 }]" data="data.name" uc-input />
    

      该组件的验证规则是长度验证:长度介于6-12之间,这里就不详细介绍angular的验证组件实现过程了(稍后再另外写一篇文章来介绍吧)。

      有了前端组件的具体格式,接下来便可以着手编码后端代码来支持了。

    后端

      首先看一下长度验证类,代码如下:

    //验证类
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public class StringLengthAttribute : Attribute
    {
        public int MinLength { get; private set; }
    
        public int MaxLength { get; private set; }
    
        public StringLengthAttribute(Int32 maxLength) : this(maxLength, 0) { }
    
        public StringLengthAttribute(Int32 maxLength, Int32 minLength)
        {
            this.MinLength = minLength;
            this.MaxLength = maxLength;
        }
    
        public override bool Valid(object value)
        {
            var valueLength = (value ?? string.Empty).ToString().Length;
            return valueLength >= this.MinLength && valueLength <= this.MaxLength;
        }
    }
    
    //模型
    public class EnterModel
    {
        [StringLength(12, 5)]
        public string Name { get; set; }
    }
    

      虽然有模型且模型已经添加了验证,已经可以用来生成组件html了,但是有了真实环境中的验证规则是多种多样的,因此仍然需要一个接口,提供一个将验证转化成thml的方法。

      为了兼容原来的easyui,于是我新增了相应的angular类去继承StringLengthAttribute和转换的接口,代码如下:

    public interface IValidationConverter
    {
        string ToHtml();
    }
    
    public class AngularStringLengthAttribute : StringLengthAttribute, IValidationConverter
    {
        public AngularStringLengthAttribute(int maxLength) : base(maxLength) { }
    
        public AngularStringLengthAttribute(int maxLength, int minLength) : base(maxLength, minLength) { }
    
        public string ToHtml()
        {
            var sb = new StringBuilder(" valid-type="['stringLength', {");
            return sb.AppendFormat(" min: {0},", this.MinLength)
                .AppendFormat(" max: {0}", this.MaxLength)
                .Append(" }]"").ToString();
        }
    }
    

      虽然也有方法可以不增加IValidationConverter接口,直接在生成angular组件的方法中遍历Attribute并判断属于何种验证类型,但是需要switch来区分,并生成相应的html,这样就违反了单一责任原则了,因此还是使用接口比较方便,模型代码调整如下:

    public class EnterModel
    {
        [AngularStringLength(12, 5)]
        public string Name { get; set; }
    }
    

      接下来只需要再编写一个类来生成angular的验证组件,方法的实现无非就是需要传入属性名以及绑定的名称而已,那么页面绑定的方式为:<%= Angular.Input("属性名", "绑定名") %>。

      如果想要让绑定方式支持表达式的话,如:<%= Angular.Input<TModel>(m => m.Name, "绑定名") %>,那么就需要从表达式中解析出属性名,《获取Lambda表达式内表达式的值 》这篇文章有讲解。

      以上生成angular组件html的方法这里就写了,留给各位来实现了。

      有了以上的验证支持,接下来只要在mvcHandler内添加相关的验证支持,上一篇中我们已经实现从请求中提取路由方法所需要的对象,因此只需要在接下来对该对象进行验证即可,如果验证通过则调用路由方法,如果不正确则调用Error,mvcHandler验证代码如下:

    var isValid = reqType.GetProperties().All(p =>
    {
        var attrs = p.GetCustomAttributes(false);
        if (attrs.Length == 0)
            return true;
    
        return attrs.All(attr =>
        {
            if (attr is IPropertyValidation)
                return (attr as IPropertyValidation).Valid(p.GetValue(req, null));
    
            return true;
        });
    });
    if (!isValid) {
        Setting.Error("数据不正确!").ExecuteResult(context);
        return;
    }
    

    结束语

      以上大部分的代码都是可以放入公用库的,我只是开了一个头,后面大部分的工作还要根据具体的项目来进行调整,种类繁多的验证以及组件,需要开发人员和美工的相互配合才能完成。

      那么就到这里了,如果有什么疑问或者问题,欢迎留言,谢谢。

  • 相关阅读:
    接入SDK
    GSoap的使用(调用webservice接口)
    NSString/NSMutableString 字符串处理
    iOS Base SDK & Deployment Target 区别&设置
    No identities are available for signing 的解决办法
    GitHub 代码托管Mac下
    代理&Block 传值
    UItextField 的简单控制
    NSPredicate 的使用
    tableView 删除一行后下一行点击事件被忽略
  • 原文地址:https://www.cnblogs.com/ahl5esoft/p/5087741.html
Copyright © 2020-2023  润新知