真正走進ASP.NET MVC的世界,才知道它的精彩。
“拋棄WebService,在.NET4中用 jQuery 調用 WCF”——原來拋棄WebService之後,還可以用jQuery調用ASP.NET MVC的Controller。
“Ajax為主的應用不需要ASP.NET MVC”,原來Ajax的世界更需要ASP.NET MVC。
曾經天真的想法,在實踐中證明了它的天真,但在從天真到事實的過程中,得到的是成長。
下面就談談我是如何認識到這個的。相比於結論,其中的過程更重要。
還是以之前文章中的部落格園站內短訊息功能(顯示目前使用者短訊息列表)為例,開始用的是jQuery外掛程式Templates進行列表資料繫結,後來遇到了兩個問題:
1) 在綁定時需要根據條件判斷產生不同的元素,比如使用者發過來的短訊息,寄件者顯示為連結,如果是系統通知,則顯示為文本。Templates對這樣的操作處理起來不是很方便;
2) 綁定後的資料無法在服務端重用。有時從搜尋引擎友好或者使用者體驗的角度,在頁面第一次載入時,不用ajax,在頁面載入後點擊重新整理或分頁連結時,才使用ajax。這樣就要在服務端與用戶端分別維護資料繫結操作。
也就是說原來服務端返回的是實體類對象列表,現在要返回的是將資料與Html組裝起來的字串。
1. 開始我們考慮的是一種醜陋的方法,用StringBuilder進行字串拼接產生資料繫結結果,伺服器端WCF服務中的代碼如下:
顯然這種方法易出錯,維護性差。
2. 接著我們考慮了第二種方法(參考自Render User Control as String Template),通過Web User Control產生字串。WCF服務中的代碼如下:
Page page = new Page();
Control control = page.LoadControl("~/Controls/MsgListControl.ascx");
((IRenderable<List<SiteMsg>>)control).PopulateData(siteMsgList);
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter htw = new HtmlTextWriter(sw))
{
control.RenderControl(htw);
return sb.ToString();
}
}
由於MsgListControl.ascx的類型是動態編譯產生的,所以無法通過強制類型轉換將control轉換為MsgListControl類型,然後傳遞資料給它。
這裡需要通過一個另外的IRenderable<T>介面來實現資料的綁定,MsgListControl實現了這個介面,代碼如下:
public partial class MsgListControl : UserControl, IRenderable<List<SiteMsg>>
{
public void PopulateData(List<SiteMsg> siteMsgList)
{
rptMsgList.DataSource = siteMsgList;
rptMsgList.DataBind();
}
}
public interface IRenderable<T>
{
void PopulateData(T data);
}
在WCF服務中通過調用介面中的PopulateData方法進行資料的綁定。
這個方法增加了額外的介面,顯得有些複雜。
3. 後來我們想到了ASP.NET MVC,雖然不熟悉,但要嘗試一下,看能否更好地解決這個問題。
於是,上ASP.NET MVC 3,用Razor,咱們也MVC一把。
應用情境:在現有的VS2010 Web Site項目中應用ASP.NET MVC 3。MsgController收到請求後,由Inbox(一個Action)將包含短訊息列表的整個整頁模式返回給用戶端;當使用者點擊頁面的重新整理或者分頁連結時,通過Ajax發起POST請求以擷取短訊息列表,MsgController收到請求後,由List(一個Action)將短訊息列表的視圖返回給用戶端。
期望的效果:短訊息列表視圖能重用,Inbox與List使用的是同一個視圖。
一開始遇到了兩個小問題:
a) MapRoute配置之後,訪問出現" HTTP Error 404.0 - Not Found"錯誤。原因是訪問的網址沒有檔案名稱,未走ASP.NET管線。解決方案是在web.config的system.webServer中加上以下的配置:
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true" />
b) 繼續訪問,出現“The resource cannot be found.”錯誤。解決方案:由於用的是Web Site項目,要將Controllers檔案夾移至App_Code。
然後進入MVC相關代碼編寫,先從Ajax調用部分開始。
Controller的代碼如下:
public class MsgController : Controller
{
[HttpPost]
public ActionResult List(SiteMsgQuery msgQuery)
{
List<SiteMsg> siteMsgList = GetInboxMsgList(msgQuery);
return View("MsgList", siteMsgList);
}
}
需要注意的就一個地方:[HttpPost],既然是Ajax調用,當然要響應POST請求。
View的代碼(MsgList.cshtml)如下:
@using CNBlogs.UcHome.ExternalService.MsgWcfService
@model List<SiteMsg>
@foreach(SiteMsg msg in Model){
<div class="msg_item">
<div class="msg_sender">@msg.SenderName</div>
<div class="msg_title"><a href='/msg/item/@msg.id/'>@msg.Subject</a></div>
<div class="msg_sendtime">@msg.SendTime.ToString("yyyy-MM-dd HH:mm")</div>
</div>
}
比在.ascx中寫起來方便多了。
用戶端js調用代碼如下:
function GetMsgList(pageIndex, pageSize) {
var msgQuery = {}
msgQuery.PageIndex = pageIndex;
msgQuery.PageSize = pageSize;
$.ajaxSettings.dataType = 'plain/text';//不要用json
$.ajaxSettings.url = '/msg/list';
$.ajaxSettings.data = '{"msgQuery":' + JSON.stringify(msgQuery) + '}';
$.ajaxSettings.success = function (data) {
$("#msg_list").html(data);
};
$.ajax();
}
需要注意的是兩個地方(因為伺服器端Controller返回的不是json格式的資料):
a) dataType不要用json,用jQuery預設的就行,如果指定的話,就用plain/text;
b) 返回資料就在data中,不要通過data.d擷取。
這樣,用ASP.NET MVC就輕鬆搞定Ajax調用,比之前的WCF, StringBuider, .ascx都要方便。
原來在ASP.NET MVC中使用Ajax如此方便,完全可以取代以前用的WCF中轉站。
解決了Ajax的問題,接著處理整個頁面的顯示。
在頁面的View中直接重用剛才Ajax所用的View就行了,範例程式碼如下:
View(Inbox.cshtml):
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
@Html.Partial("MsgList")
</body>
</html>
Control:
public class MsgController : Controller
{
public ActionResult Inbox()
{
SiteMsgQuery msgQuery = new SiteMsgQuery()
{
PageIndex = 1,
PageSize = 30
};
List<SiteMsg> siteMsgList = GetInboxMsgList(msgQuery);
return View("Inbox", siteMsgList);
}
}
搞定!真的很方便!想要的解決方案就是它--ASP.NET MVC!
在這裡為我的錯誤觀點“Ajax為主的應用不需要ASP.NET MVC”向大家道歉!請大家諒解!
好好學習,不進則退!