一、前言

  说来惭愧,做了几年ASP.NET最近才有机会使用MVC开发生产项目。项目中新增、编辑表单提交存在大量服务端数据格式校验,各种if else显得代码过于繁琐,特别是表单数据比较多的时候尤为恶心,正好今天比较闲就写了一个Demo,统一验证Model层中的数据格式。在此说明一下,MVC自带数据检验功能同时还能主动通知到前端显示,个人感觉不太好用的样子(并没有深入研究),而且公司项目并没有使用MVC的辅助方法生成View,不知道MVC的数据校验功能能否起作用。

二、目标

  通过调用对象的Validate方法,校验对象的属性是否全部合法,否则返回一条失败信息。

三、文件结构

  

四、实现

  1、因为并不是所有的Model都应该有Validate方法,同时Validate方法的逻辑代码也是唯一的(解析对象属性上的特性,根据特性描述的规则校验数据是否合法),因此定义接口IValidate,并为IValidate绑定扩展方法Validate,待校验Model继承自IValidate接口(数据解析使用到的特性定义代码在后面)

public interface IValidate
{
}
public static class ValidateExtension
{
/// <summary>
/// 校验对象属性值是否合法
/// </summary>
/// <param name="obj">待校验对象</param>
/// <returns></returns>
public static ValidateResult Validate(this IValidate obj)
{
ValidateResult result = new ValidateResult(); PropertyInfo[] infos = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo p in infos)
{
//获取数据校验特性。
Attribute[] attrs = Attribute.GetCustomAttributes(p, typeof(ValidateAttribute), false);
if (attrs.Length <= )
{
continue;
} //获取名称描述特性
CaptionAttribute caption = Attribute.GetCustomAttribute(p, typeof(CaptionAttribute), false) as CaptionAttribute;
object value = p.GetValue(obj); foreach (Attribute attr in attrs)
{
ValidateAttribute validate = attr as ValidateAttribute;
if (validate == null)
{
continue;
} result = Validate(validate, value, caption);
if (!result.IsSuccess)
{
return result;
}
}
}
return result;
} /// <summary>
/// 校验数据是否合法
/// </summary>
/// <param name="validate">校验规则</param>
/// <param name="value">待校验值</param>
/// <param name="caption">描述</param>
/// <returns></returns>
static ValidateResult Validate(ValidateAttribute validate, object value, CaptionAttribute caption)
{
ValidateResult result = new ValidateResult(); if (!validate.Validate(value))
{
result.IsSuccess = false;
if (caption == null)
{
result.ErrorMessage = validate.GetErrorMessage();
}
else
{
result.ErrorMessage = validate.GetErrorMessage(caption.Name);
}
}
return result;
}
}

  2、定义特性,首先我们应该需要一个描述属性名称的特性CaptionAttribute,用来描述该属性的名称,使得数据异常提示更为友好。

[AttributeUsage(AttributeTargets.Property)]
public class CaptionAttribute : Attribute
{
/// <summary>
/// 构造方法
/// </summary>
/// <param name="name">属性名称</param>
public CaptionAttribute(string name)
{
this.Name = name;
} /// <summary>
/// 属性名称
/// </summary>
public string Name { get; set; }
}

  3、定义特性,校验规则特性应该都有一个Validate方法,校验当前规则是否可以同过,同时我们在解析规则的时候读取属性特性也应该只读取校验相关的特性,而不是所有的。因此定义特性父类ValidateAttribute。

[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public abstract class ValidateAttribute : Attribute
{
/// <summary>
/// 校验不通过提示信息
/// </summary>
protected string ErrorMessage { get; set; } /// <summary>
/// 校验数据是否合法
/// </summary>
/// <param name="value">待校验的值</param>
/// <returns></returns>
public abstract bool Validate(object value); /// <summary>
/// 获取检验不通过提示信息
/// </summary>
/// <param name="name">字段名称</param>
/// <returns></returns>
public string GetErrorMessage(string name = "")
{
if (string.IsNullOrEmpty(name))
{
name = "该字段";
} return string.Format(this.ErrorMessage, name);
}
}

  4、定义特性,创建相关的业务规则特性,为了用户使用时可自定义提示信息,应重载构造方法(此处提供值范围规则及正则验证规则)。

public class RangeAttribute : ValidateAttribute
{
private int min = -;
private int max = -; /// <summary>
/// 构造方法
/// </summary>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
public RangeAttribute(int min, int max)
: this(min, max, string.Format("{0}应在{1}到{2}之间", "{0}", min, max))
{ } /// <summary>
/// 构造方法
/// </summary>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
/// <param name="errorMessage">校验失败提示信息</param>
public RangeAttribute(int min, int max, string errorMessage)
{
this.min = min;
this.max = max;
this.ErrorMessage = errorMessage;
} /// <summary>
/// 校验数据是否合法
/// </summary>
/// <param name="value">待校验的值</param>
/// <returns></returns>
public override bool Validate(object value)
{
if (value == null)
{
return false;
} decimal v;
if (!decimal.TryParse(value.ToString(), out v))
{
return false;
} return v >= min && v <= max;
}
}
public class RegexAttribute : ValidateAttribute
{
private string regex = null; /// <summary>
/// 构造方法
/// </summary>
/// <param name="regex">正则</param>
public RegexAttribute(string regex)
: this(regex, "{0}格式错误")
{ } /// <summary>
/// 构造方法
/// </summary>
/// <param name="regex">正则</param>
/// <param name="errorMessage">校验失败提示信息</param>
public RegexAttribute(string regex, string errorMessage)
{
this.regex = regex;
this.ErrorMessage = errorMessage;
} /// <summary>
/// 校验数据是否合法
/// </summary>
/// <param name="value">待校验的值</param>
/// <returns></returns>
public override bool Validate(object value)
{
if (value == null)
{
return false;
} return new Regex(regex).IsMatch(value.ToString());
}
}
public class RegexExpression
{
/// <summary>
/// 手机号格式正则表达式(开放号段:13|4|5|7|8)
/// </summary>
public const string Mobile = "^1(3|4|5|7|8)\\d{9}$"; /// <summary>
/// 中文字符正则表达式(只允许输入中文且不包含任何标点符号等)
/// </summary>
public const string Chinese = "^[\u4E00-\u9FFF]+$"; /// <summary>
/// 邮箱正则表达式
/// </summary>
public const string Email = @"^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$";
}

  5、Validate方法返回的结果包含是否成功以及如果失败了则返回提示信息,因此定义一个校验结果类(差点就漏了这部分代码...)。

public class ValidateResult
{
public ValidateResult()
{
this.IsSuccess = true;
} /// <summary>
/// 构造方法
/// </summary>
/// <param name="isSuccess">是否校验通过</param>
/// <param name="errorMessage">检验不通过提示信息</param>
public ValidateResult(bool isSuccess, string errorMessage)
{
this.IsSuccess = isSuccess;
this.ErrorMessage = errorMessage;
} /// <summary>
/// 是否校验通过
/// </summary>
public bool IsSuccess { get; set; } /// <summary>
/// 检验不通过提示信息
/// </summary>
public string ErrorMessage { get; set; }
}

五、使用

  1、定义Model

class User : IValidate
{
[Caption("手机号码")]
[Regex(RegexExpression.Mobile)]
public string Mobile { get; set; } [Caption("年龄")]
[Range(, )]
public int Age { get; set; } [Range(, , "身高数据异常")]
public decimal Height { get; set; }
}

  2、调用验证及输出结果

  

  

  

使用Attribute校验对象属性数据是否合法的更多相关文章

  1. 区分元素特性attribute和对象属性property

    × 目录 [1]定义 [2]共有 [3]例外[4]特殊[5]自定义[6]混淆[7]总结 前面的话 其实attribute和property两个单词,翻译出来都是属性,但是<javascript高 ...

  2. PHP校验日期格式是否合法

    在后端开发中,我们常常需要校验前端传入参数的合法性,如果是校验日期参数,我们可以通过下面的方法来校验: /** * 校验日期格式是否合法 * @param string $date * @param ...

  3. SpringMVC使用校验validator校验对象属性

    1.pom.xm添加依赖 <dependency> <groupId>javax.validation</groupId> <artifactId>va ...

  4. js校验对象是否全部为空

    function judgeIsNotBlank(obj) { var bool = true; var arr = Object.keys(obj); ; for(var key in obj){ ...

  5. SpringMVC学习总结(三)——Controller接口详解(2)

    4.5.ServletForwardingController 将接收到的请求转发到一个命名的servlet,具体示例如下: package cn.javass.chapter4.web.servle ...

  6. go-common-pool设计原理分析

    common-pool: 对于一些对象的频繁创建会带来很大的系统开销,并且需要对对象数量进行控制来降低资源消耗,比如数据库连接,线程等 common-pool采用了缓存思想来解决这个问题,预先把一些对 ...

  7. SpringMVC(4.2):Controller接口控制器详解(2)

    原文出处: 张开涛 4.5.ServletForwardingController 将接收到的请求转发到一个命名的servlet,具体示例如下: package cn.javass.chapter4. ...

  8. Controller接口控制器2

    5.ServletForwardingController 将接收到的请求转发到一个命名的servlet,具体示例如下: package cn.javass.chapter4.web.servlet; ...

  9. 盈创动力之 JS校验方法

    var IS_NULL = 128; // 10000000var IS_FULL = 64; // 01000000var IS_HALF = 32; // 00100000var IS_ASCII ...

随机推荐

  1. 附录C 编译安装Hive

    如果需要直接安装Hive,可以跳过编译步骤,从Hive的官网下载编译好的安装包,下载地址为http://hive.apache.org/downloads.html . C.1  编译Hive C.1 ...

  2. Hadoop入门学习笔记---part2

    在<Hadoop入门学习笔记---part1>中感觉自己虽然总结的比较详细,但是始终感觉有点凌乱.不够系统化,不够简洁.经过自己的推敲和总结,现在在此处概括性的总结一下,认为在准备搭建ha ...

  3. Windows Phone 如何在程序中播放提示声音?

    在Windows Phone 中播放提示音可以使用 Microsoft.Xna.Framework.Audio 命名空间下的 SoundEffect 类.具体使用方法如下: 1. 根据声音文件路径创建 ...

  4. php 数组动态添加实现代码(最土团购系统的价格排序)

    最近在实现最土团购系统的价格排序功能,需要对$oc数组进行扩展,经过测试用下面的方法即可. 核心代码如下: <?php $now=time(); $oc = array( 'team_type' ...

  5. IOS学习笔记之获取Plist文件读取数据

    @property(nonatomic,strong) NSArray *pic; //创建数组属性 @property(nonatomic,assign) int index; //创建索引属性 @ ...

  6. assign()

  7. 数据结构:顺序表(python版)

    顺序表python版的实现(部分功能未实现) #!/usr/bin/env python # -*- coding:utf-8 -*- class SeqList(object): def __ini ...

  8. 用python来个百度关键词刷排名脚本

    目的:写个脚本来提升百度排名 我一个seo届前辈的朋友找我,他说,seo事无巨细,自己主观方面能做的几乎都能做了,提升百度等搜索引擎中的排名往往效果不佳或者起效周期慢.能不能人为去干预下呢? 获得排名 ...

  9. MyBatis的mapper

    在前面的学习中,我们还在写一些接口啊,实现类啊,是不是感觉好low的... 其实,我们是可以不用写接口的实现类的,今天就带着大家一起学习一下,当然,我是回顾的. 看下面的结构,是不是没实现类呢! 源码 ...

  10. C++构造函数中不能调用虚函数

    在构造函数中调用虚函数,并不会产生多态的效果,就跟普通函数一样. c++ primer 第四版中497页15.4.5构造函数和析构中的虚函数讲到,如果在构造函数或析构函数中调用虚函数,则运行的是为构造 ...