ASP.NET MVC中的Json Binding和Validate
電子商務網站支付功能頁面往往會有很多信息,對于這些信息的保存,往往是分步完成的,那么使用Ajax最合適不過了,比如其中的收貨人信息模塊。這些信息的新建和編輯保存都是用Ajax來完成的。那么有幾種方式完成這個操作呢,我想到如下幾種。
先來看看該功能的截圖:

一般情況下這些信息會對應一個實體類,就命名為:ReceiverInfo,簡單起見,我定義ReceiverInfo如下:

1、將需要的值拼接成json文本,再Action里面處理
首先您需要將要保存的值拼接成一個json文本,類似:
然后用Jquery保存到數據庫,代碼如下:
url: "/Home/test1",
type: "post",
cache: false,
data: test
});
然后您在Action里面這樣操作:
string bodyText = reader.ReadToEnd();
JavaScriptSerializer js = new JavaScriptSerializer();
ReceiverInfo receiver = js.Deserialize<ReceiverInfo>(bodyText);
//保存。。。
2、利用自定義的ModelBinder實現
JsonBinder
2 {
3 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
4 {
5 StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
6 string json = reader.ReadToEnd();
7
8 if (string.IsNullOrEmpty(json))
9 return json;
10
11 JavaScriptSerializer serializer = new JavaScriptSerializer();
12 object jsonData = serializer.DeserializeObject(json);
13 return serializer.Deserialize<T>(json);
14 }
15 }
我們繼承IModelBinder接口,實現其 方法:
即可。我們可以在Action里面這樣使用:
這樣我們自定義的 IModelBinder就會取代DefaultModelBinder完成數據綁定。
3、直接傳遞一個Json對象
上面兩種方法并沒有利用MVC的System.ComponentModel.DataAnnotations進行有效的數據驗證。您可能需要自己手動驗證,無疑增加了工作量。
我們試試這種方式。
前端的寫法:
2. ReceiverId: 5,
3. ReceiverName: "will",
4. Sex: "F",
5. CreateDate: "2011-02-21"};.ajax({
6. url: "/Home/test1",
7. type: "post",
8. cache: false,
9. data: b,
10. success: function(data) { alert(data.message); },
11. error: function(xhr, a, b) { alert
12. (xhr.responseText); }});
Action的寫法:
我們能正常的得到綁定后的數據。而且我們還能利用System.ComponentModel.DataAnnotations進行數據驗證。我們為ReceiverInfo做如下改動:
public string ReceiverName { get; set; }
并在前端為ReceiverName賦值為空字符串,再次執行,得到提示:

很好,不過我們有新的要求了,那就是傳遞更復雜的對象,比如對象套嵌對象,對象有集合屬性,這種方式不能勝任了。
4、利用MvcFutures的JsonValueProviderFactory
每一版的MVC都有一個MvcFutures,里面會有一些額外的功能,這些功能有些會加入下一個版本中,而這些功能在某些時候很有用處。我查看了里面的類,發現有一個類JsonValueProviderFactory正是處理復雜對象的提交和數據驗證。
由于json對象需要特定解析才能使用默認的DefaultModelBinder,而這個解析過程需要在ValueProvider階段完成,所以需要實現特定的ValueProvider給DefaultModelBinder。我們需要實現一個ValueProviderFactory和IValueProvider,而MVC里面的DictionaryValueProvider<TValue>(繼承了IValueProvider)已經足夠使用了,所以只需要繼承ValueProviderFactory實現其方法:public override IValueProvider GetValueProvider(ControllerContext controllerContext)即可,具體代碼您可以看JsonValueProviderFactory。
我們定義另一個類:
ReceiverInfoChild
{
[System.ComponentModel.DataAnnotations.Required(ErrorMessage = "ChildId必須填寫")]
public string ChildId { get; set; }
}
并為類ReceiverInfo增加一個屬性public List<ReceiverInfoChild> ReceiverInfoChild { get; set; }
我們把JsonValueProviderFactory拿出來放在項目里面,然后在Global.asax里面注冊一下,就可以使用了。
2. AreaRegistration.RegisterAllAreas();
3. RegisterRoutes(RouteTable.Routes);
4. ValueProviderFactories.Factories.Add(new
5. JsonValueProviderFactory());}
因為JsonValueProviderFactory中有:if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))來判斷進來的請求是不是json對象,所以我們提交數據的時候需要這樣寫:
2. {
3. ReceiverInfoChild: [{ ChildId: "1" }, { ChildId: "11"}],
4. ReceiverId: 5,
5. ReceiverName: "will",
6. Sex: "F",
7. CreateDate: "2011-02-21"
8. },
9. {
10. ReceiverInfoChild: [{ ChildId: "2" }, { ChildId: "22"}],
11. ReceiverId: 5,
12. ReceiverName: "will",
13. Sex: "F",
14. CreateDate: "2011-02-21" }
15. ];.ajax({
16. url: "/Home/test1",
17. type: "post",
18. cache: false,
19. contentType: "application/json;charset=utf-8",
20. data: JSON.stringify(ReceiverInfo),
21. success: function(data) { alert(data.message); },
22. error: function(xhr, a, b) { alert(xhr.responseText); }});
其中JSON.stringify(ReceiverInfo)是將json對象轉換成字符串,您可以到這里下載該類庫。
在Action里面,我們這樣寫就可以了:
看一下調試的結果:

完全正常綁定了值。我們再看看數據驗證:

至此,我們實驗了四種方案:
第一種方案,最麻煩,而且容易出錯(可能跟我個人不喜歡拼接字符串有關系);
第二種方案,有一定的通用性,但是不利于數據驗證;
第三種方案,通用,可以進行有效的數據驗證,應對一般的需求夠用了,但是處理更復雜的對象不行;
第四種方案,幾乎可以處理我們遇到的所有情況
另外,這是在ASP.NET MVC2中的使用,到了ASP.NET MVC3,微軟已經把JsonValueProviderFactory作為內置的功能了。

