這算是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,然后錯誤提示“你輸入的不是數值類型”吧]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM