再说表单验证,在Web Api中使用ModelState进行接口参数验证
写在前面
上篇文章中说到了表单验证的问题,然后尝试了一下用扩展方法实现链式编程,评论区大家讨论的非常激烈也推荐了一些很强大的验证插件。其中一位园友提到了说可以使用MVC的ModelState,因为之前通常都在Web项目中用没在Api项目用过,想想Api方法接收的多参数都封装成了一个实体类,独立于数据Model层,这样其实很方便用ModelState做验证,于是尝试了一下。
认识ModelState
我们都知道在MVC中使用ModelState实现表单验证非常简单,借助jquery.validate.unobtrusive这个插件就能轻松的在页面上输出错误信息,详细的介绍可以参考这篇文章《[Asp.net MVC]Asp.net MVC5系列--在模型中添加验证规则》。但是在WebApi中没有视图页让我们来展示错误信息,那要怎么捕获到验证失败的信息并作为请求结果返回给请求端呢?以前学MVC的时候也没有深究ModelState是什么机制实现验证,为什么用Html.ValidationMessageFor就能输出错误信息?这次就系统的了解一下,那就先看看ModelState到底是什么鬼。转到它的定义发现它就是一个Dictionary:

为了看个究竟,打开Reflector找到ModelStateDictionary,发现它有这些属性:
// Properties
public int Count { get; }
public bool IsReadOnly { get; }
public bool IsValid { get; }
public ModelState this[string key] { get; set; }
public ICollection<string> Keys { get; }
public ICollection<ModelState> Values { get; }
那这里的Keys装的就是被验证的Model的属性啦,Values就是对应key的值(ModelState类型)了。再看看ModelState类型是个什么鬼:
[Serializable]
public class ModelState
{
// Fields
private ModelErrorCollection _errors; // Methods
public ModelState(); // Properties
public ModelErrorCollection Errors { get; }
public ValueProviderResult Value { get; set; }
}
看它有两个属性Errors和Values,从它们的类型名称就能看出到底是干嘛的了。Errors装的就是验证失败的错误信息(具体就是一个ModelError),继续看到底包含写什么东西:
[Serializable]
public class ModelError
{
// Methods
public ModelError(Exception exception);
public ModelError(string errorMessage);
public ModelError(Exception exception, string errorMessage); // Properties
public string ErrorMessage { get; private set; }
public Exception Exception { get; private set; }
}
啊~看到ErrorMessage瞬间觉得哈皮啊,这就是我们需要返回去的鬼东西!
可是为什么是Collection呢?那肯定啊,因为一个字段可以有多个验证规则,比如有Required还有MaxLength等等。Value装的就这个字段的值,具体就是一个ValueProviderResult,具体里面是什么就不贴代码了,因为有什么和本文没太大关系,自己回去偷偷看就好了。关于模型是怎么验证的错误信息是怎么绑上去的,看以看看Artech的Model验证系统运行机制是如何实现的?,超详细的解说。好了,来龙去脉都摸清楚了,那就开始码代码,主要就是手动把错误信息抓出来。
代码实现
以登录场景为例,为登录接口封装了一个登录模型,并加上验证规则:
public class MemberLogin
{
/// <summary>
/// 登录手机号
/// </summary>
[Required(ErrorMessage = "请输入手机号码")]
[RegularExpression(@"^1[3|4|5|7|8][0-9]\d{8}$", ErrorMessage = "手机号格式错误")]
public string Phone { get; set; } /// <summary>
/// 验证码key
/// </summary>
[Required(ErrorMessage = "验证码无效")]
public string CodeKey { get; set; } /// <summary>
/// 验证码值
/// </summary>
[Required(ErrorMessage = "请输入短信验证码")]
public string CodeValue { get; set; }
}
然后在接口里第一行加上:
if (!ModelState.IsValid)
{
string error = string.Empty;
foreach (var key in ModelState.Keys)
{
var state = ModelState[key];
if (state.Errors.Any())
{
error = state.Errors.First().ErrorMessage;
break;
}
}
return ApiResponse(new ReturnMessage() { Status = ResultStatus.Failed, Message = error });
}
主要思路就是:验证失败后遍历ModelState的Key,如果这个被验证的字段至少有一项验证失败(ModelError),那么就拿到第一个ErrorMessage,然后就结束遍历,因为取到所有的也没什么用,也方便前端对结果进行处理。
用swagger的接口调式工具发起请求,得到响应如下:

CodeValue也是空的但是没有返回错误信息,是因为在取错误信息的时候取到第一条后就break了。
到这里貌似大功告成了,但仔细一想,每个接口里都要写这么大一坨重复代码,真是很难受,那怎么搞?没错,MVC里有个神奇的东西-Filter,WebApi完整地沿用了这一优秀的特性,用比较高端的说法就是面向切面编程(AOP)中的分离横切点的思想,从而实现代码复用。那就创建一个Attribute类并继承System.Web.Http.Filters .ActionFilterAttribute,然后重写OnActionExecuting方法,具体内容就是刚才那一大坨稍微调整一下,完整代码为:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method , Inherited = true)]
public class ModelValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var modelState = actionContext.ModelState;
if (!modelState.IsValid)
{
string error = string.Empty;
foreach (var key in modelState.Keys)
{
var state = modelState[key];
if (state.Errors.Any())
{
error = state.Errors.First().ErrorMessage;
break;
}
}
ReturnMessage response = new ReturnMessage() { Status = ResultStatus.Failed, Message = error };
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Accepted)
{
Content = new StringContent(JsonConvert.SerializeObject(response), System.Text.Encoding.GetEncoding("UTF-8"), "application/json")
};
}
}
}
然后在接口上打上[ModelValidationAttribute]这么个标签就ok了。当然了,这个Attribute我指定了使用范围包含Class,直接打在Controller上面也是阔以滴~这样就不用每个Action都写了。
写在最后
没有上一篇的分享,就不会收到大家的建议,也许就不会有这次的实践,所以,分享就意味着收获!
再说表单验证,在Web Api中使用ModelState进行接口参数验证的更多相关文章
- Web API中的模型验证
一.模型验证的作用 在ASP.NET Web API中,我们可以使用 System.ComponentModel.DataAnnotations 命名空间中的属性为模型上的属性设置验证规则. 一个模型 ...
- Web API中的模型验证Model Validation
数据注释 在ASP.NET Web API中,您可以使用System.ComponentModel.DataAnnotations命名空间中的属性为模型上的属性设置验证规则. using System ...
- ASP.NET Core Web API中带有刷新令牌的JWT身份验证流程
ASP.NET Core Web API中带有刷新令牌的JWT身份验证流程 翻译自:地址 在今年年初,我整理了有关将JWT身份验证与ASP.NET Core Web API和Angular一起使用的详 ...
- ASP.NET Web API中的JSON和XML序列化
ASP.NET Web API中的JSON和XML序列化 前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok ...
- ASP.NET Web API中的Controller
虽然通过Visual Studio向导在ASP.NET Web API项目中创建的 Controller类型默认派生与抽象类型ApiController,但是ASP.NET Web API框架本身只要 ...
- WEB API 中HTTP的get、post、put,delete 请求方式
一.WEB API 中HTTP 请求方式的四个主要方法 (GET, PUT, POST, DELETE), 按照下列方式映射为 CURD 操作: 1.POST 用于新建资源,服务端在指定的URI 上创 ...
- Web Api 中Get 和 Post 请求的多种情况分析
转自:http://www.cnblogs.com/babycool/p/3922738.html 来看看对于一般前台页面发起的get和post请求,我们在Web API中要如何来处理. 这里我使用J ...
- 【ASP.NET Web API教程】5.5 ASP.NET Web API中的HTTP Cookie
原文:[ASP.NET Web API教程]5.5 ASP.NET Web API中的HTTP Cookie 5.5 HTTP Cookies in ASP.NET Web API 5.5 ASP.N ...
- .NET Core WEB API中接口参数的模型绑定的理解
在.NET Core WEB API中参数的模型绑定方式有以下表格中的几种: 微软官方文档说明地址:https://docs.microsoft.com/zh-cn/aspnet/core/web-a ...
随机推荐
- C#中string.format用法详解
C#中string.format用法详解 本文实例总结了C#中string.format用法.分享给大家供大家参考.具体分析如下: String.Format 方法的几种定义: String.Form ...
- thrift:swift项目笔记
先声明:此swift不是Apple公司的那个swift开发语言,而是facebook的另一个开源项目. facebook的thrift IDL文件,如果默认用thrift -gen java生成jav ...
- 华为5G空口新技术(2015年)
2015-03-24 长江后浪推前浪,4G建设方兴未艾,业界关于5G的讨论已如火如荼.对于每一代移动通信,空口技术都相当于王冠上的明珠. 在月初的世界移动通信大会上,华为发布了面向5G的新空口,并展出 ...
- 5G承载为什么需要三层到边缘
- SQL Server 2012附加数据库时,错误提示如下:尝试打开或创建物理时,CREATE FILE 遇到操作系统错误 5(拒绝访问。)
错误提示:Create File遇到操作系统错误5(拒绝访问) 解决方案: 在所有程序-SQL Server 2012-"SQL Server 配置管理器",点击"SQL ...
- 【C#】透屏幕,屏幕扩展
if (!SCREEN_STATE) { ) { System.Windows.Forms.Screen s2 = System.Windows.Forms.Screen.AllScreens[]; ...
- Python进制转换
一 内置函数 bin().oct().hex()的返回值均为字符串,且分别带有0b.0o.0x前缀. 实例 统计二进制数里1的个数 def countBits(n): return bin(n).co ...
- Django中 如何修改models字段的默认值
场景描述: 在建表的时候,新闻表的内容简介字段,给的默认值是可以为空的,但是没有设置默认值为blank,那么,这种情况下,对于只有标题,没有内容简介的新闻来讲,发布之后,前端显示的内容简介位置是non ...
- memcache占用CPU过高的解决办法
Simon最近为公司服务器操碎了心 , 先是mysqld进程占用CPU过高 , 导致服务器性能变低 ,网站打开太慢.通过增加max_connections及table_cache解决了问题 ,随后发现 ...
- linux安装nexus
一.下载nexus 最新地址在:https://www.sonatype.com/download-oss-sonatype 我下载的是:nexus-2.14.1-01-bundle.tar.gz,是 ...