专栏首页大内老A这算是ASP.NET MVC的一个大BUG吗?

这算是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,然后错误提示“你输入的不是数值类型”吧]

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 深入理解string和如何高效地使用string

    无论你所使用的是哪种编程语言,我们都不得不承认这样一个共识:string是我们使用最为频繁的一种对象。但是string的常用性并不意味着它的简单性,而且我认为,...

    蒋金楠
  • ASP.NET MVC集成EntLib实现“自动化”异常处理[实例篇]

    个人觉得异常处理对于程序员来说是最为熟悉的同时也是最难掌握的。说它熟悉,因为仅仅就是try/catch/finally而已。说它难以掌握,则是因为很多开发人员却...

    蒋金楠
  • 字符串的驻留(String Interning)

    关于字符串的驻留的机制,对于那些了解它的人肯定会认为很简单,但是我相信会有很大一部分人对它存在迷惑。在开始关于字符串的驻留之前,先给出一个有趣的Sample: ...

    蒋金楠
  • 从零开始学C++之标准库类型(一):string 类简介和例程

    一、标准库string类型 string类型支持长度可变的字符串,C++标准库将负责管理与存储字符相关的内存,以及提供各种有用的操作 ,在VC中直接F1查看 ...

    s1mba
  • C# 基础知识系列- 14 IO篇 文件的操作(2)

    除了上文提到的 GetDirectories 方法可以直接返回目录下所有子目录以外,还有一组方法也可以枚举出当前目录下的子目录:

    程序员小高
  • 从0.5到1写个rpc框架 - 2:远程服务调用(grpc)

    gRPC是Google开源的跨语言远程服务调用(RPC)框架,通信协议用的HTTP/2,数据传输默认用的protocol buffers(一种轻便高效的结构化数...

    acupt
  • 细说Golang的JSON解析

    之前一直写一些动态语言,觉得解析JOSN还是很简单的,往往只需要几行代码就能拿到解析好的JSON对象。Go语言自带的json包可以让您在程序中方便的读取和写入 ...

    mojocn
  • golang微信支付服务端

    一般来说,使用golang主要还是写服务端。所以本文主要讲golang在处理微信移动支付的服务端时的统一下单接口和支付回调接口,以及查询接口。 微信支付流程 下...

    李海彬
  • .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”

    FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为...

    蒋金楠
  • AS1.0(2.0)中的XML示例

    虽然Flash早就升级为AS3.0,但是FMS的服务端编程依然仅支持AS1.0(2.0),服务端与.net通讯的最简单方式莫过于请求一个RESTful的webS...

    菩提树下的杨过

扫码关注云+社区

领取腾讯云代金券