使用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 ...
随机推荐
- android 官方DrawerLayout的介绍和使用
南尘:爱编程,爱安卓,每天进步一点点. drawerLayout是Support Library包中实现了侧滑菜单效果的控件,可以说drawerLayout是因为第三方控件如MenuDrawer等的出 ...
- CentOS6.5网络设置
CentOS6.5网络设置 不知道哪里做错了,长时间无法连接网络,百度了各种还是不可以.最后自己提取了以前可以联网的配置粘贴过来,成功.配置文件内容如下: vim /etc/resolv.conf 1 ...
- web性能优化:详说浏览器缓存
TOC 背景 浏览器的总流程图 一步一步说缓存 朴素的静态服务器 设置缓存超时时间 html5 Application Cache Last-Modified/If-Modified-Since Et ...
- 7.1数据注解属性--Key【Code-First系列】
Key特性可以被用到类的属性中,Code-First默认约定,创建一个主键,是以属性的名字“Id”,或者是类名+Id来的. Key特性重写了这个默认的约定,你可以应用Key特性到一个类的属性上面,不管 ...
- javascript设计模式实践之迭代器--具有百叶窗切换图片效果的JQuery插件(一)
类似于幻灯片的切换效果,有时需要在网页中完成一些图片的自动切换效果,比如广告,宣传,产品介绍之类的,那么单纯的切就没意思了,需要在切换的时候通过一些效果使得切换生动些. 比较常用之一的就是窗帘切换了. ...
- Castle.ActiveRecord多数据库配置
最近使用Castle.ActiveRecord框架,网上关于多数据支持的文章很少,因此有了这篇博文的产生. 开发工具VS2015,Sql Server2008R2 新建数据库,数据初始化脚本如下: - ...
- KB,Kb单位换算,网络带宽中的Kbps和KB/s到底是什么意思? (注:B和b的区别)
B是指字节(Byte)1个字节有8个比特组成 b是指比特(bit)代表一个2进制位(值为0或1) 上过网的朋友应该会听说过网络带宽这个词,可是这个网络带宽的单位到底是什么,为什么有的人说Kbps ...
- Lind.DDD.ExpressionExtensions动态构建表达式树,实现对数据集的权限控制
回到目录 Lind.DDD框架里提出了对数据集的控制,某些权限的用户为某些表添加某些数据集的权限,具体实现是在一张表中存储用户ID,表名,检索字段,检索值和检索操作符,然后用户登陆后,通过自己权限来构 ...
- Lind.DDD.Events领域事件介绍
回到目录 闲话多说 领域事件大叔感觉是最不好讲的一篇文章,所以拖欠了很久,但最终还是在2015年年前(阴历)把这个知识点讲一下,事件这个东西早在C#1.0时代就有了,那时学起来也是一个费劲,什么是委托 ...
- 18 行 JS 代码编一个倒时器
有时候在生活中,你需要一个JavaScript倒计时时钟,而不是一个末日装置设备.不管你是否有一次约会,销售.促销.或者游戏,你可以受益于使用原生JavaScript构建一个时钟,而不是拿到一个现成的 ...