WebApi 参数验证,参数绑定后进行验证
事件作用在Model绑定之后,action执行之前。有时候我们要检查model绑定的内容,比如是否为空,长度,大小,格式等等。
最简单的属性必选判定,需要在Model上面设定 [Required] 也可以设定异常说明的内容
http://localhost:20138/api/Demo
public class Person { [Required(ErrorMessage ="Name属性必填")] public string Name { get; set; } }
public HttpResponseMessage Get([FromUri]Person person) { if (ModelState.IsValid) { return this.Request.CreateResponse(HttpStatusCode.OK); } else { return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState); } }
或许有人会好奇, 页面显示的和属性设定的不同。这是因为还没到进一步那证那一步
如果URL是http://localhost:20138/api/Demo?Name,那么显示
同样还有验证字符串最大长度
[StringLength(50)]
常用的还有 数字范围 [Range(18, 25],指定字符串格式 正则[RegularExpression(
"^[1]+[3,4,5,7,8]+\\d{9}"]
使用异常提示模板进行提示,后面要修改的时候还可以不用修改代码,定义在资源文件中,使用占位符
先设定有占位符的资源,其次需要在类属性上设定
public class Person { [Required(ErrorMessage ="Name属性必填")] public string Name { get; set; } [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources))] [Range(18, 25, ErrorMessageResourceName = "Range", ErrorMessageResourceType = typeof(Resources))] public int? Age { get; set; } }
public HttpResponseMessage Get([FromUri]Person person) { if (ModelState.IsValid) { return this.Request.CreateResponse(HttpStatusCode.OK); } else { return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState); } }
http://localhost:20138/api/Demo?Name=12345678901&age=55
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources))] [Range(18, 25, ErrorMessageResourceName = "Range", ErrorMessageResourceType = typeof(Resources))] public int? Age { get; set; }
我们还可以自定义参数验证规则,比如验证 属性必须是某几个固定的值之一,比如设定性别
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] public class DomainAttribute : ValidationAttribute { public IEnumerable<string> Values { get; private set; } public DomainAttribute(string value) { this.Values = new string[] { value }; } public DomainAttribute(params string[] values) { this.Values = values; } public override bool IsValid(object value)// 验证是否有效value是传递的值 { return this.Values.Any(item => value.ToString() == item); } public override string FormatErrorMessage(string name)// name 是栏位名称,验证失败走这一段 { string[] values = this.Values.Select(value => string.Format("'{0}'", value)).ToArray(); return string.Format(base.ErrorMessageString, name,string.Join(",", values)); } }
http://localhost:20138/api/Demo?Name=12345678901&age=22&Gender=Z
public class Person { [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources))] [Domain("M", "F", "m", "f", ErrorMessageResourceName = "Domain", ErrorMessageResourceType = typeof(Resources))] public string Gender { get; set; } }
同时我们可以验证 同一个类,不同属性之间的比较,
http://localhost:20138/api/Demo?Name=Jerry&Grade=G7&Salary=5000
根据不同级别的人,判定工资应该在哪个范围之内
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WebApi { public class Employee { public string Name { get; set; } public string Grade { get; set; } [RangeIf("Grade", "G7", 2000, 3000)] [RangeIf("Grade", "G8", 3000, 4000)] [RangeIf("Grade", "G9", 4000, 5000)] public decimal Salary { get; set; } } }
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace WebApi { [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] // AlloMultiple=true 一个属性上可以加多个相同属性 public class RangeIfAttribute : RangeAttribute //继承 数字范围的内 { public string Property { get; set; } public string Value { get; set; } public RangeIfAttribute(string property, string value, double minimum,double maximum) // 第一个值property,是 获取需要比较的其他属性的名称,这里指Grade : base(minimum, maximum) { this.Property = property; this.Value = value ?? ""; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) // Value 是通过URL获取的值 5000, { PropertyInfo property = validationContext.ObjectType.GetProperty(this.Property); // Property 是参数验证通过构造函数传进来的Grade 等级 object propertyValue = property.GetValue(validationContext.ObjectInstance, null); propertyValue = propertyValue ?? ""; if (propertyValue.ToString() != this.Value) //构造函数传进来的等级与 参数属性验证的等级比较,不一样的PASS。一样的则进行比较,通过URL传进来的5000,等级G7,与 参数验证相同等级G7对应的2000, 3000进行比较 { return ValidationResult.Success; } return base.IsValid(value, validationContext); } private object typeid; public override object TypeId { get { return typeid ?? (typeid = new object()); } } } }
通过过滤器优化参数验证
每次我们都要在action内验证 Model绑定后 ModelState.IsValid 是否false,比较麻烦,我们可以定义filter,在model绑定后,action开始前进行判断
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Web; using System.Web.Http.Controllers; using System.Web.Http.Filters; using System.Net.Http; namespace WebApi { public class ValidateAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { if (!actionContext.ModelState.IsValid) { actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState); } base.OnActionExecuting(actionContext); } } }
在controller 上面修饰 过滤器,同时 action的返回值可以有 HttpResponseMessage 改为 void,意思一样
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web; using System.Web.Http; using System.Web.Mvc; using WebApi.Models; namespace WebApi.Controllers { [Validate] public class DemoController : ApiController { public void Get([FromUri]Employee employee) { } } }
参数验证还有一种自绑定方式,在类里面进行参数验证
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; using WebApi.Properties; namespace WebApi.Models { public class Person : IValidatableObject { public string Name { get; set; } public string Gender { get; set; } public int? Age { get; set; } public IEnumerable<ValidationResult> Validate( ValidationContext validationContext) { Person person = validationContext.ObjectInstance as Person; if (null == person) { yield break; } if (string.IsNullOrEmpty(person.Name)) { yield return new ValidationResult("'Name'是必需字段",new string[] { "Name" }); } if (string.IsNullOrEmpty(person.Gender)) { yield return new ValidationResult("'Gender'是必需字段",new string[] { "Gender" }); } else if (!new string[] { "M", "F" }.Any(g => string.Compare(person.Gender, g, true) == 0)) { yield return new ValidationResult("有效'Gender'必须是'M','F'之一", new string[] { "Gender" }); } if (null == person.Age) { yield return new ValidationResult("'Age'是必需字段",new string[] { "Age" }); } else if (person.Age > 25 || person.Age < 18) { yield return new ValidationResult("有效'Age'必须在18到25周岁之间",new string[] { "Age" }); } } } }