使用Attribute校验对象属性数据是否合法
一、前言
说来惭愧,做了几年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校验对象属性数据是否合法的更多相关文章
- 区分元素特性attribute和对象属性property
× 目录 [1]定义 [2]共有 [3]例外[4]特殊[5]自定义[6]混淆[7]总结 前面的话 其实attribute和property两个单词,翻译出来都是属性,但是<javascript高 ...
- PHP校验日期格式是否合法
在后端开发中,我们常常需要校验前端传入参数的合法性,如果是校验日期参数,我们可以通过下面的方法来校验: /** * 校验日期格式是否合法 * @param string $date * @param ...
- SpringMVC使用校验validator校验对象属性
1.pom.xm添加依赖 <dependency> <groupId>javax.validation</groupId> <artifactId>va ...
- js校验对象是否全部为空
function judgeIsNotBlank(obj) { var bool = true; var arr = Object.keys(obj); ; for(var key in obj){ ...
- SpringMVC学习总结(三)——Controller接口详解(2)
4.5.ServletForwardingController 将接收到的请求转发到一个命名的servlet,具体示例如下: package cn.javass.chapter4.web.servle ...
- go-common-pool设计原理分析
common-pool: 对于一些对象的频繁创建会带来很大的系统开销,并且需要对对象数量进行控制来降低资源消耗,比如数据库连接,线程等 common-pool采用了缓存思想来解决这个问题,预先把一些对 ...
- SpringMVC(4.2):Controller接口控制器详解(2)
原文出处: 张开涛 4.5.ServletForwardingController 将接收到的请求转发到一个命名的servlet,具体示例如下: package cn.javass.chapter4. ...
- Controller接口控制器2
5.ServletForwardingController 将接收到的请求转发到一个命名的servlet,具体示例如下: package cn.javass.chapter4.web.servlet; ...
- 盈创动力之 JS校验方法
var IS_NULL = 128; // 10000000var IS_FULL = 64; // 01000000var IS_HALF = 32; // 00100000var IS_ASCII ...
随机推荐
- ZOJ Problem Set - 1331 Perfect Cubes 判断一个double是否为整数
zju对时间要求比较高,这就要求我们不能简单地暴力求解(三个循环搞定),就要换个思路:因为在循环时,已知a,确定b,c,d,在外重两层循环中已经给定了b和c,我们就不用遍历d,我们可以利用d^3=a^ ...
- MySQL入门01-MySQL源码安装
操作系统:CentOS 6.7 MySQL版本:5.6.30 1.前期准备 2.系统配置 3.CMake编译配置 4.make && make install 5.后期配置和测试 Re ...
- ListView中的数据表格写入Excel中
SaveFileDialog sfd = new SaveFileDialog(); sfd.DefaultExt = "xls"; sfd.Filter = "Exce ...
- 微信小程序(应用号)开发体验
昨天微信小程序(应用号)内测的消息把整个技术社区炸开了锅, 我也忍不住跟了几波,可惜没有内测资格,听闻破解版出来了, 今天早上就着原来的项目资源试开发了一下,总结一下体验. 总体体验 开发效率高,6: ...
- jQuery获取Select选中的Text和Value
获取Select选中的Text和Value语法解释:$("#select_id").change(function(){//code...}); // 为Select添加事件, ...
- .net中xml文件的导入使用(包括创建xml和导入xml)
上次有说到.net 创建xml文件的方法(一种固定方式,一种动态方法),这次记录一下怎样导入xml文件 1.导入xml文件的方法 1)xml文件格式
- UIAlertController 部分用法及属性
//创建UIAlertController:初始化UIAlertController 需要使用alertControllerWithTitle UIAlertController *alertCont ...
- Integer.valueof(String s)和Integer.parseInt(String s)的具体区别是什么?
Integer.valueof(String s)和Integer.parseInt(String s)的具体区别是什么? Integer.valueof(String s)是将一个包装类是将一个实际 ...
- python基础之异常处理
Python3 错误和异常 作为Python初学者,在刚学习Python编程时,经常会看到一些报错信息,在前面我们没有提及,这章节我们会专门介绍. Python有两种错误很容易辨认:语法错误和异常. ...
- Lind.DDD.Caching分布式数据集缓存介绍
回到目录 戏说当年 大叔原创的分布式数据集缓存在之前的企业级框架里介绍过,大家可以关注<我心中的核心组件(可插拔的AOP)~第二回 缓存拦截器>,而今天主要对Lind.DDD.Cachin ...