这篇博客真是干货,干得估计还有点“磕牙”,所以还提供视频和代码。但基础稍弱的同学,怕还是得自行补充一些基础知识——就一篇文章,确实没办法面面俱到。

  • 视频和代码下载:Demo - 百度云盘 · 一起帮
  • 参考原文:Automatic ModelState validation in ASP.NET MVC(英文强,基础好的同学可以直接看原文了)。

缘起

我忘了是不是在园子里讲过,我命名为“截断式编程”的写法。其主要目的,就是把简单的、过滤条件、“非主干的”逻辑放在最前面。比如在ASP.NET MVC的Action中,处理POST时,我们通常都要进行服务端验证,于是我们就可以这样写:

        [HttpPost]public ActionResult Send(MessageSendModel model){#region 非截断式写法//if (ModelState.IsValid)//{//    //假设发送了一个消息//    Response.Write("消息已经发送");//}//return View(model);#endregion#region 截断式编程//过滤条件if (!ModelState.IsValid){return View(model);}//主干程序:假设发送了一个消息Response.Write("消息已经发送");return RedirectToAction("Send");#endregion}

View Code

由于采用了这种写法,我们很快就发现了一个问题:if (!ModelState.IsValid) { return View(model); }到处都是。

是不是有点“坏味道”的感觉?你是不是想怎么“弄”它一下?

ActionFilter

首先想到的,当然就是ActionFilter了:在Action执行之前,用一个Filter进行检查,不就OK了吗?

我觉得这个想法不错,但是,但是,请注意,一定要问一个为什么!为什么别人想不到呢?——这怎么可能?!

所以,在自己动手之前,养成习惯,google/bing一下,看看别人是怎么弄的。

果不其然,找到一篇博客::Automatic ModelState validation in ASP.NET MVC ,和我的思路一模一样!\(^o^)/

而且,他想得比我更周全!看得我那个兴奋啊……

所以,这里我们得到的第一个经验:动手之前先搜一搜,不要重复造轮子

再引申开一点,

英文 + google = 伟大的程序员。

至少在目前,以及可预见的将来,对于开发人员而言,英语非常重要,非常重要,重要性怎么强调都不为过。大家可以试一下,有没有中文的类似的博客资料等,我没去试。但根据我的经验,相比于英文资料,中文资料是非常匮乏的。

关于使用搜索引擎,很多同学觉得这是一种“可耻的行为”,但其实不然。你一定要明白:你的目的是解决问题,而不是炫技,非得把什么东西都记在脑子里,非得什么都自己写出来……算了,这话可能很多人不接受,篇幅有限,懒得说了。懂的人一点就通,不懂的人你怎么说都没用。

最简单的情形

一开始解决方案还是比较简单的。我就直接放代码了:

public class ValidateModelStateAttribute : ActionFilterAttribute
{       public override void OnActionExecuting(ActionExecutingContext filterContext){if (!viewData.ModelState.IsValid){filterContext.Result = new ViewResult();}}
}

View Code

理解的难点大概就在于为什么:return View(); 和 filterContext.Result = new ViewResult(); 是等价的。

我觉得有这个问题的根源还是没有理解“面向对象”,这一直是.NET阵营程序员所缺乏的。

一起帮的QQ群里有时候就有同学犯迷糊,聊了之后,我就明白了,他始终把return View();认为是“转到那个View页面”的意思,而没有能够理解成:这就是一个方法,返回的是一个ActionResult对象。大家能明白我的意思吧?所有的Action都是一个方法,一个返回ActionResult对象的方法,然后ASP.NET MVC框架根据这个ActionResult对象,找到相应的View进行呈现(Render)。这中间多了一个环节,但这不是“脱了裤子放屁”,是非常有必要的,而且非常“精妙”的一个架构设计。

你看,这样Filter里就可以通过给filterContext.Result赋值而达到页面呈现的效果。当然,这么做最大的好处还在于UI层的“可测试化”,这又是一个非常庞杂的话题,此处先略过。

多说一句,一定要说这一句:感谢这位同学提出问题,让我知道作为初学者,那些地方是难点。话说,我做直播这么久,要收到点反馈可真难啊!

 

然而,这里有一个问题

如果没有bing(话说不能google真特么的悲剧)一下,我肯定就到此为止了。然而,高手就是高手,Ben Foster想到了另外一个问题:

假设页面中需要由后台传来的数据才能正常呈现时,如何在Filter里赋值,再传递给View?

这样说有点晕,所以我才专门弄了一个Demo,用DropdownList做例子。DropdownList的可选项数据是从后台获取的,由于HTTP的“无状态协议”特征,POST不能“继承”GET时获取的数据,咋办呢?

TempData解围

很多种办法,Ben Foster的博客里是封装一个方法,GET和POST都调用。

我以前项目,是利用的MVC中的TempData,在两个请求见共享数据。这样,如果可选项数据是从数据库获取的,可以减少一次查询,有一点点性能上的提高。

TempData真是一个非常好的东西,这就是我喜欢.NET的原因,它非常的“贴心”,给人的感觉是“始终面向一线开发人员”的,它知道你在写代码的时候可能会遇到什么问题,从而事先给你准备好解决方案。

思路和实现都很简单:GET的时候,就把POST需要的数据存到TempData中;POST的时候,就把TempData中的数据取出来(需要一个强制转换,因为数据是存放为Object类型的)。

代码就不贴了,因为这不是最终的解决方案。

但FilterAction里肿么办

在Controller里面你怎么玩都行,但我们现在要“封装”啊,我们要在Filter里解决这个问题啊!

傻眼了。关键的问题在于我们需要在OnActionExecuting(Action执行之前)进行验证,而此时Action的ViewModel并没有生成,我们可以从ControllerBase.ActionParameters 中取值,但取出来的是一个Object类型,你不知道要把它转换成什么类型的(当然你可以用反射做,但非常复杂,复杂到你都觉得没有必要),TempData也是一样的问题。

每次这个时候,我就会想起ASP.NET WebForm中的ViewState来,那些年认为它是“性能杀手”,对它口诛笔伐。MVC的诞生并流行,估计和这玩意就有非常大(多大呢?我乱说的,三成吧)的关系。

但现在MVC没ViewState了,要自己处理,呵呵,又有点怀恋以前WebForm开发的“便捷”了。

再展开来说,DateSet,Linq to SQL,Entity Framework……一系列的技术,一经推出,都是吵吵嚷嚷,不可开交。其实何必呢,各有各的用处,各有各的适用场景,脱离了具体的业务要求,能争出个什么高下来?

所以我一直强调,软件工程,技术选择以解决问题为标准。问题千差万别,所以使用的技术不可能整齐划一,一句话,“没有银弹”。

PRG模式

如果是我的话,这时候就放弃了。但Ben Foster给出了一个非常棒的解决方案:利用PRG模式。

PRG是Post, Redirect, Get的缩写,意思是所有的POST请求,都Redirect到GET的Action,哪怕返回的实际上是同一个页面。示例代码如下:

        [HttpPost]public ActionResult Send(MessageSendModel model){//主干程序:假设发送了一个消息Response.Write("消息已经发送");//注意:不是return View()//1、是Redirect//2、重定向的这个Action是和自己同名的,都是Sendreturn RedirectToAction("Send");}

View Code

我非常欣慰的是我们的系统架构,一开始就是按照这个原则搭建的。最早接触PRG模式的时候,我也是有点迷迷糊糊的,但想到大家都这样用,而且看起来也没什么坏处,就采用了,后来真的是,一次又一次的发现这种模式的便利。这次又是这样搭上了“便车”。

记得我在直播里说过的:如果架构中出现了很多稀奇古怪难以克服的问题,一般来说,就是因为你没走在大道上。

通用的惯例模式,就是大道,别人已经走过的路啊。你循着别人走过的道走,碰到问题的时候,也一样会比较容易的找到解决问题的方案;你要独辟蹊径——通常情况下可能不是你想独辟蹊径,呵呵,多半是自己走岔了吧——那荒山野岭的,确实很难找到求助。

 

终极方案

思路就是:

  • 在Action的POST的Filter里,如果未通过验证,就把ModelState存放在TempData之中;
  • 给Aciton的GET也添加一个Filter,用于取出TempData中的ModelState,并Merge到自己的ModelState中。
  • 于是,通过Redirect,GET的Action得到了错误提示信息

非常清晰,我图片和代码混用吧:

最核心代码:

        /// <summary>/// Exports the current ModelState to TempData (available on the next request)./// </summary>       protected static void ExportModelStateToTempData(ControllerContext context){context.Controller.TempData[Key] = context.Controller.ViewData.ModelState;}/// <summary>/// Populates the current ModelState with the values in TempData/// </summary>protected static void ImportModelStateFromTempData(ControllerContext context){var prevModelState = context.Controller.TempData[Key] as ModelStateDictionary;context.Controller.ViewData.ModelState.Merge(prevModelState);}

View Code

就这么简单,完美!回味无穷。

其实,在POST的Action里return View();并不是一个好套路。

在我的项目我就发现了这么一个问题:当return的View()里还含有@Html.Action()调用,且该调用需要区分GET和POST时,会进入POST所属的ChildAction,这肯定是不符合逻辑的。(表述起来好吃力!慢慢看,一边看一边想,想不明白的看视频吧……)

题外话

磨蹭了一天,终于写完了。

写得好累,感觉写这一篇文章比那90分钟的视频还累,难怪现在好多开源项目都没文档,直接上视频了……

最后说说“一起帮”吧,现在已经被当初的乞丐版强多了,隔三差五的也有同学在上面提问,算是有些生气了,但人气还是远远的不够,根据QQ群里投票结果,现在主要精力应放在做推广上。

推广了好几天了,有点效果,但唉呀我的妈呀!这推广,比写这篇博客还累,咋整?

园子里就不说这些了。“酒向知己饮,诗向会人吟”,以后博客园只会讲技术,这也是博客园欢迎的。想聊点编程以外的,欢迎加QQ群:

179742319(付费入群抢红包),312423951(验证入群)

以及关注微信公众号:我们一起帮

感谢评论区

@敲代码的吃货:

如果采用PRG的方案,就不能达到你的要求:“把提交上来的数据再次呈现以便修改的”。要满足这种需求,只能在POST里return View(model); model里是带着之前数据的。

@stoneniqiu:

该方案的优势不是“减少”代码量,而是从架构层面,解决POST中return View(); 造成的空异常问题。

转载于:https://www.cnblogs.com/freeflying/p/6870158.html

受够了if (ModelState.IsValid)?ActionFitlter也是一路的坑啊!相关推荐

  1. 柯洁:我受够了AI围棋

    鱼羊 萧箫 发自 凹非寺 量子位 | 公众号 QbitAI "我受够了AI围棋." 中日韩三国围棋擂台赛赛后,完败于韩国棋手申真谞的柯洁,在社交媒体上留下了这样的字句. 这场比赛中 ...

  2. 一款java代码生成器(我受够了加班),走起!

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 先以一首 ...

  3. 柯洁:我受够了AI围棋;联发科校园招聘,应届生年薪约45万元;Chrome 100发布 | 极客头条...

    「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧. 整理 | 苏宓 出品 | CSDN(ID:CSDNnews) ...

  4. 为什么mvc里面的ModelState.IsValid一只都是true

    http://zhidao.baidu.com/link?url=H69JQBpF8vbJEOUUc1RCjRZZ05gSGn6PiPL740aGgR3qIfFTT__pt4KgEg7O47lReYR ...

  5. ASP.NET MVC里ModelState.IsValid总是true或者总是false

    概述 model的属性如果不加验证相关的特性,ModelState.IsValid会永远为true:如果加了验证相关的特性,不满足验证规则时,ModelState.IsValid为false.接收到参 ...

  6. ModelState.IsValid忽略型别的检查错误

    Web Api在Int或DateTime如果传空值的话会自动帮忙设预设值,但是在ModelState.IsValid的时候,却会出现型别上的错误. 解决方式 把Model改成正确,也就是预设允许可以为 ...

  7. 抵制百度网盘!!我受够了。你他吗干脆一个字节一个字节给人下载吧

    抵制百度网盘!!我受够了.你他吗干脆一个字节一个字节给人下载吧抵制百度网盘!!我受够了.你他吗干脆一个字节一个字节给人下载吧抵制百度网盘!!我受够了.你他吗干脆一个字节一个字节给人下载吧抵制百度网盘! ...

  8. 假如有一天,程序猿受够了所有压迫……

    有一天程序猿们受够了这个世界 他们不但每天要加班 还会遭受产品经理的压迫 还要时刻提防被拉去「祭天」 遂决定联合起义 - 这一天所有的程序猿都受收到了一封邮件 "你还在日夜加班吗?" ...

  9. 程序员受够了所有压迫之后……

    有一天程序员们受够了这个世界 他们不但每天要加班 遭受产品经理的压迫 还要时刻提防被拉去「祭天」 遂决定联合起义 - 这一天所有的程序员都受收到了一封邮件 "你还在日夜加班吗?" ...

最新文章

  1. Python知识点1——基础
  2. 谈谈集群NAS在VDI存储中的应用
  3. 编程实现算术表达式求值_用魔法打败魔法:C++模板元编程实现的scheme元循环求值器...
  4. 算法与数据结构--空间复杂度O(1)遍历树
  5. Linux 浅谈代码打印到终端的缓冲区(进度条程序)
  6. SpringBoot读取配置文件
  7. windows平台vs2010编译64位libiconv与libxml2
  8. Create a Search Scope for a Sharepoint 2010 List or Library
  9. 【React】react实现前端播放m3u8格式视频
  10. 从“老公”的称呼来历,看男人地位的变迁!
  11. 开会的五有五不四框架
  12. 软件工程第一次团队作业
  13. 40万奖金 | 阿里云天池第三届数据库性能大赛,广邀英才挑战创新上云
  14. 设计模式之路 | 建造者模式
  15. map之containsKey方法
  16. Dynamics CRM 数据迁移工具DataMigrationUtility
  17. 苹果电脑视频声音提取的方法
  18. 王刚口述滴滴的创业经历
  19. 测试显卡的软件叫游戏什么,显卡测试软件哪个好
  20. python当前时间命名图片,python+opencv保存图片以系统时间命名怎么敲?

热门文章

  1. 程序员编程艺术第二十七章:不改变正负数相对顺序重新排列数组(无解?)
  2. 函数指针及其的运用(上)——何为函数指针
  3. Linux系统的各种安装方法与修复技巧
  4. ICCV 2019 | 旷视提出MetaPruning:基于元学习和AutoML的模型压缩新方法
  5. Python凭啥这么受欢迎?
  6. c语言程序设计电子图书 汉诺塔,用C写的汉诺塔(hanoi)程序
  7. 一份清华大佬的代码模版,简洁易懂!
  8. oracle空间整理,ORACLE数据库空间整理心得
  9. matlab+awgn和wgn,噪聲強度(噪聲功率) 噪聲方差到底有什么關系? matlab中的awgn函數...
  10. RGB-D dataset