这算是ASP.NET MVC的一个大BUG吗?
这是昨天一个同事遇到的问题,我觉得这是一个蛮大的问题,而且不像是ASP.NET MVC的设计者有意为之,换言之,这可能是ASP.NET MVC的一个Bug(不过也有可能是保持原始请求数据而作的妥协)。StackOverflow上也有对这个问题的描述http://stackoverflow.com/questions/1775170/asp-net-mvc-modelstate-clear
闲话少说,我们通过一个简单的问题重新这个问题。首先我们 定义了如下一个默认的HomeController,它具有一个默认Action方法Index。该方法接受一个类型为DemoModel的参数,定义其中的逻辑非常简单:我们对该参数的三个属性略加修改后,将其作为Model呈现在对应的View中。
public class HomeController : Controller { public ActionResult Index(DemoModel model) { model.Foo += ":Changed"; model.Bar += ":Changed"; model.Baz += ":Changed"; return View("index", model); } } public class DemoModel { public string Foo { get; set; } public string Bar { get; set; } public string Baz { get; set; } }
对于Action方法Index对应的View(Index.cshtml),我们可以采用如下三种定义方式将Model对象以编译模式呈现出来。
//第一种形式
@model DemoModel
@Html.LabelFor(m=>m.Foo)
@Html.TextBoxFor(m => m.Foo)
@Html.LabelFor(m => m.Bar)
@Html.TextBoxFor(m => m.Bar)
@Html.LabelFor(m => m.Baz)
@Html.TextBoxFor(m => m.Baz)
//第二种形式
@model DemoModel
@Html.LabelFor(m=>m.Foo)
@Html.EditorFor (m => m.Foo)
@Html.LabelFor(m => m.Bar)
@Html.EditorFor (m => m.Bar)
@Html.LabelFor(m => m.Baz)
@Html.EditorFor (m => m.Baz)
//第三种形式
@model DemoModel
@Html.EditorForModel
现在我们运行该程序,并通过Query String的形式提供作为Action方法Index参数的数据(?foo=123&bar=456&baz=789),我们可以看到界面上呈现出来的总是原始值,也就是说我们在Action方法Index中对原始数据的修改没有起到任何效果。
通过查看ASP.NET MVC框架自身的代码,我想这个问题的根源应该源于InputExtensions类型的InputHelper方法。如下所示,当InputHelper在指定表单元素值得时候,会先从当前ModelState中获取,如果该值在ModelState中不存在,才会从当前ViewData中获取。对于本例来说,ModelState中的值是原始值,ViewData的值采用修改后的值。
public static class InputExtensions { private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes); } private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes) { … switch (inputType) { … default: { string str4 = (string) htmlHelper.GetModelStateValue(fullHtmlFieldName, typeof(string)); tagBuilder.MergeAttribute("value", str4 ?? (useViewData ? htmlHelper.EvalString(fullHtmlFieldName, format) : str2), isExplicitValue); goto Label_016C; } } … }
我觉得rinsen的评论说得有道理,这也可能是为了保持请求的原始数据而作的妥协。不过我还是觉得这样的设计有违MVC的基本原则,MVC处理请求的流程很清楚:客户端(浏览器)向定义在Controller中的某个Action方法发送请求,Action方法处理这个请求,并呈现出相应的View来对请求做最后的响应。换言之,最终呈现怎么的View应该完全由Action方法决定,对于我们的例子来说,Action方法很明显的意图就是将更新过的Model呈现出来。而且这是一种非常典型的场景:服务端对原始数据进行简单的加工后再呈现出来。
其实我觉得严格来说也是无奈之举吧,
拿Update场景来说
比如说Model里面的某个Property可能是Int的,但是你传入的Form值却可能是任意的字符串,这时后台ModelState.IsValid是false,然后你就需要返回View让用户继续修改,并把用户输入的值带入到Form中。
这时候model其实是有的(反正不为null,而Property也是有默认值的:0)而回显显然不可能回显为0,而是用户的输入。
所以这个角度看来ModelState的优先级是比较高的。
[你总不能回显一个0,然后错误提示“你输入的不是数值类型”吧]
相关文章
- iOS 那些不为人知的bug: Error Domain=NSCocoaErrorDomain Code=3840
- 宝塔面板Linux系统通过Docker部署VB.NET Asp.Net Core WebAPI应用
- 解决一个bug需要分几步
- spring jdbctemplate 启动报错(oracle驱动bug)详解数据库
- .netLinux下部署ASP.NET环境指南(linux部署asp)
- 利用Oracle.Net实现数据库互联(oracle.net)
- .net让Oracle与ASP.NET实现无缝连接(oracleasp)
- #Bug何其多#一个macOS Catalina的新bug可能会删掉你的电子邮件
- .Net程序开发利用Redis提高效率(.net使用redis)
- Fedora Linux 中有 Bug 吗?一起来修复它!
- .net利用MSSQL连接ASP.NET的办法探索(mssql 连接asp)
- NET环境下操作Oracle数据库的技巧(.net操作oracle)
- 数据库NET开发者操作Oracle数据库的全攻略(net操作oracle)
- CMySQL与NET技术融会贯通(c mysql .net)
- 快速搭建 NET 项目,用 MySQL 配置环境(.net 配置mysql)
- NET环境下 MySQL数据库应用实践(.net配合 mysql)
- NET备份MySQL提升数据安全性(.net 备份mysql)
- NET和MySQL的无缝搭配实现创新的技术路径(.net mysql支持)
- Net平台下MySQL数据库操作实践(.net mysql操作)
- Oracle临时表BUG 遭遇不测崩溃惊魂(oracle临时表bug)
- Redis无法安装NET组件(redis装不上net)
- asp.NET开发中正则表达式中BUG分析
- asp.net(C#)动态添加非ASP的标准html控件(如添加Script标签)
- asp页面和Asp.net页面传中文参数UrlEncode编码以及接收解码
- jquerycheckbox勾选的bug问题解决方案与分析