ASP.NET MVC 初體驗


MVC系列文章終於開始了,前段時間公司項目結束后一直在封裝一個html+ashx+js+easyui的權限系統,最近差不多也完成了,遲些時候會分享源碼給大家。當然這個MVC系列結束后如果時間允許還會另寫一個MVC + EF + ExtJs的權限系統作為總結,希望有興趣的朋友保持關注。

本系列文章使用的環境是:VS2010 + 4.0 Framework、sql 2008企業版、MVC3、EF4.1
注:VS2010不帶MVC3,ASP.NET MVC 3 RTM下載地址。裝的時候vs不要打開,先裝MVC3,再裝漢化包。安裝中不要強制取消,就算取消也要等回滾完,否則下次vs啟動不了。

 

一、ASP.NET MVC 3 小試牛刀

新建項目 - 創建mvc3 web應用程序 - 模板選空、視圖引擎Razor、不使用HTML5,建好項目后,解決方案是這樣的:

和傳統的webForm項目結構完全不一樣,這里不寫任何代碼直接Ctrl+F5跑下程序,會得到:

webForm里直接運行項目會列出已有的文件讓你點選然后運行,當然也可以在頁面上右鍵 - 在瀏覽器中查看,mvc項目里無法通過在.cshtml視圖文件上右鍵 - 在瀏覽器中查看的方式運行頁面,而是通過【路由】找控制器Controller里的Action,Action再調用指定的視圖View呈現頁面。因此運行MVC項目只能Ctrl+F5或者F5調試的方式運行程序,要看不同的頁面需要在路由里進行調整。后續會有文章詳細講解路由,這里僅快速理解下MVC:
M Model:實體模型 / 視圖模型(ViewModel);
V View:視圖。簡單說就是html頁面,這個html頁面接收強類型的數據,方便展示數據;
C Controller:控制器。處理瀏覽器發過來的請求,根據url過來的路徑去控制器里尋找對應的Action響應瀏覽器請求。

解決上面404錯誤問題先得看看Global.asax里默認路由的定義:

routes.MapRoute(
    "Default", // 路由名稱
    "{controller}/{action}/{id}", // 帶有參數的 URL
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 參數默認值
);

只有一個路由,按照路由匹配自上到下的順序,那么本程序使用的就是這個名為Default的路由。根據這個路由的定義瀏覽器有類似這種Url:localhost:****/abc/def/e 請求過來自然就和這個路由匹配({controller}/{action}/{id})上了,那么找的是名稱為abc的Controller,調用abcController里的名為def的Action,呈現的是def這個Action里指定的視圖View。當然這種Url:localhost:**** 請求過來也能和這個名為Default的路由匹配上,因為這個默認路由定義了默認值:HomeController、Index Action、Id值可選。

到這里就很明顯知道為何是404了,因為既沒有HomeController,也沒有Index這個Action,同時也沒有任何視圖View。在解決方案里的Controller目錄上右鍵 - 創建控制器,為了和這個路由的定義搭上,控制器的名字就是HomeController,是一個空控制器。新建就好了后打開HomeController.cs:

public ActionResult Index()
{
    return View();
}

HomeController里默認就有一個叫Index的Action,並且這個Action的返回值不是void,也不是bool,也不是string,而是ActionResult。這個ActionResult就可以理解是視圖,返回一個視圖給Action(上后面的代碼直接return View();的話就是返回默認視圖,即和Action同名的Index),然后響應給控制器,最終顯示給用戶。
注:常用的還有JsonResultRedirectResult等等。具體點這里

先不返回視圖給Action,修改下返回值類型並返回一個string試試:

public string Index()
{
    return "hElLo WOrLd!!!";
}

很明顯,返回的不是ActionResult了,而是string,先按F6編譯下再Ctrl+F5跑下程序,顯示:

很容易理解:默認路由找HomeController下的名為Index的Action拿到數據顯示到瀏覽器,這里拿到的數據就是Index這個Action返回的字符串。右鍵查看下頁面的源碼,很純粹的源碼,就一個“hElLo WOrLd!!!”字符串。
注意下上圖的url:localhost:1030,其實完整的是localhost:1030/home/index(默認路由里已經指定了,所以可以省略),看的出路由里定義之后就體現在瀏覽器url里了:先找Home,再找Home下的Index。

再修改方法:

public ActionResult Index()
{
    return View();
}

然后在這個方法里右鍵 - 添加視圖 - 默認就是和Action同名的Index視圖,不使用模板。添加后項目的Views目錄下就多一個Home子目錄,目錄里有一個Index.cshtml,修改其中的代碼為:

@{
    Layout = null;
}
hElLo WOrLd!!!

Layout=null就是不使用模板。F6編譯下項目再跑下程序發現和之前顯示一樣,查看源碼也一樣。這里很好理解:Controller找Action,Action調用對應的View呈現頁面
完整的url是:localhost:1030/home/index 這里跟上面稍有不同:上面的index這個action直接就返回了一個string,未調用任何試圖,這里的index顯示的是view返回的string。

 

二、向視圖中輸出動態內容

asp、aspx等動態頁面相比於靜態的html好處就是可以根據傳遞的參數動態的顯示內容,視圖.cshtml自然也可以,可以通過ViewBag來實現,修改action:

public ActionResult Index()
{
    int hour = DateTime.Now.Hour;
    ViewBag.Greeting = hour < 12 ? "早上好" : "下午好";
    return View();
}

通過一個三元表達式給ViewBag.Greeting賦了一個值,然后在前台View加個@符號就可以調用了:@ViewBag.Greeting WOrLd!!!
F6編譯下項目再跑下程序,如願的顯示了:下午好 WOrLd!!!
注:前台和后台的ViewBag打點都是無智能提示的,只要前后台的名稱一樣即可調用。

 

三、稍復雜的例子

ok,到這基本就已經知道mvc是怎么玩的了,現在來一個稍復雜的例子,模擬邀請客人參加party的小程序:
先到解決方案的Models目錄下添加一個客人信息類:

/// <summary>
/// 客人信息類
/// </summary>
public class GuestResponse
{
    public string Name { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
    public bool? WillAttend { get; set; }  //是否參加(可空類型)
}

修改之前的Index視圖(Index.cshtml):

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <title>Index</title>
</head>
<body>
    <div>
        @ViewBag.Greeting, WOrLd!!!
        <p>
            我們將舉辦一個Party<br />
        </p>
        @Html.ActionLink("填表參加", "RsvpForm")
    </div>
</body>
</html>

跟普通html頁面不同的是Rozar語法里所有的頁面元素都是通過html輔助方法生成的,這里的@Html.ActionLink("填表參加", "RsvpForm")就是生成一個a標簽。Rozar語法簡單清爽,寫個@符號就可以開始在頁面上寫代碼了。

根據智能提示可知,第一個參數"RSVP Now"是a標簽的文字,第二個參數"RsvpForm"是這個a標簽跳轉的地址,通過Action實現跳轉自然需要添加這個Action,否則點擊又找不到視圖了:

public ViewResult RsvpForm()
{
    return View();
}

一看到return View();自然返回的是默認的視圖,即RsvpForm。繼續去添加這個視圖:在這個ResvpForm這個Action內右鍵 - 添加視圖 - 勾選上“創建強類型視圖” - 選擇GuestResponse模型類(如果找不到就先按F6編譯下項目)- 不使用模板類和腳本庫,生成的View是:

@model SimpleMVC3Demo.Models.GuestResponse
@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <title>RsvpForm</title>
</head>
<body>
    <div>
    </div>
</body>
</html>

注意第一行:@model SimpleMVC3Demo.Models.GuestResponse 這就是勾選強類型視圖的好處,直接把強類型拋到了View里,其實手動寫這一行也是一樣的效果(同樣需要編譯項目)
繼續完善RsvpForm這個視圖,在body里去掉div然后添加表單和一些文本框下拉列表供用戶填寫信息:

    @using (Html.BeginForm("RsvpForm", "Home", FormMethod.Post))
    { 
        <p>
            Your name: @Html.TextBoxFor(x => x.Name)
        </p> 
        <p>
            Your email: @Html.TextBoxFor(x => x.Email)</p> 
        <p>
            Your phone: @Html.TextBoxFor(x => x.Phone)</p> 
        <p>
            Will you attend?
            @Html.DropDownListFor(x => x.WillAttend, new[] { 
                new SelectListItem() {Text = "我會參加Party", Value = bool.TrueString}, 
                new SelectListItem() {Text = "我不參加Party", Value = bool.FalseString} 
            }, "Choose an option")
        </p> 
        <input type="submit" value="提交" /> 
    }

這里的x => x.屬性方式其實就是lambda表達式的寫法,這里不用x,用任何字母都可以,前提是必須已經把強類型的拋到View里。

這就是這個表單提交的地址,其實就等於webForm里的:

<form action="/Home/RsvpForm" method="post"> 
    <input type="text" name="username" />
    <input type="text" name="userpwd" />
    <input type="submit" value="提交" />
</form> 

這個Form提交的地址是指定的HomeController下的RsvpForm,之前已經有一個:

public ViewResult RsvpForm()
{
    return View();
}

其實提交表單處理的不是這個Action,這個是get請求的Action,需要另加一個post的Action(mvc里默認的action響應的是get方式的請求,用於請求頁面內容,響應post請求的action一般是提交表單等):

[HttpPost]
public ViewResult RsvpForm(GuestResponse guestResponse)
{
    return View("Thanks", guestResponse);
}

這只是個簡單的Action,並沒有對用戶提交的表單內容進行保存,而是直接返回了一個Thanks視圖,並且把用戶對象也就是用戶在表單里填寫的用戶信息傳到了Thanks這個視圖,來看看這個用戶對象guestResponse如何傳遞的:在這個HttpPost下面按F9加個斷點,然后按F5調試下項目,當提交表單時,這個對象其實就已經有屬性了:

很明顯這個對象已經有值了,繼續在Views目錄下的Home目錄上右鍵 - 添加視圖 - 視圖名稱Thanks - 勾選強類型視圖 - 不使用模板頁和腳本庫。ok,這個視圖也拿到了傳遞過去的用戶對象,其實就可以在body里輸出一些友好的回復:

    <div>
        <h1>
            Thank you, @Model.Name!</h1>
        @if (Model.WillAttend == true)
        { 
            @:你能來真的太好了,飲料和零食都為你准備好了!
        }
        else
        { 
            @:很遺憾你不能來。
        }
    </div>

注意看,Razor視圖引擎里可以直接寫if else邏輯語句。

寫到這里該跑下程序了,再次F6編譯下后按Ctrl + F5跑下程序。
第一個頁面:

源碼:

    <div>
        下午好, WOrLd!!!
        <p>
            我們將舉辦一個Party<br />
        </p>
        <a href="/Home/RsvpForm">填表參加</a>
    </div>

第二個頁面:

源碼:

<form action="/Home/RsvpForm" method="post">
     <p> Your name: <input id="Name" name="Name" type="text" value="" /> </p> <p> Your email: <input id="Email" name="Email" type="text" value="" /></p> <p> Your phone: <input id="Phone" name="Phone" type="text" value="" /></p> <p> Will you attend? <select id="WillAttend" name="WillAttend">
      <
option value="">請選擇</option>       <option value="True">我會參加Party</option>       <option value="False">我不參加Party</option>     </select> </p> <input type="submit" value="提交" /> </form>

第三個頁面:

源碼:

    <div>
        <h1>
            Thank you, 汪傑!</h1>
            你能來真的太好了,飲料和零食都為你准備好了!
    </div>

注意看上面兩個圖的url地址一樣,都是:localhost:1031/Home/RsvpForm
但是頁面顯示的內容不同,因為一個是get請求,一個提交表單是post請求,提交后被導向另一個視圖了。

驗證表單:剛才的表單就算任何都不填寫也可以提交,顯然不符合常理,驗證需要三步,第一去模型類里定義驗證規則:

[Required(ErrorMessage = "姓名不能為空")]
public string Name { get; set; }

[Required(ErrorMessage = "郵箱地址不能為空")]
[RegularExpression(".+\\@.+\\..+", ErrorMessage = "請輸入合法的郵箱地址")]
public string Email { get; set; }

[Required(ErrorMessage = "電話不能為空")]
public string Phone { get; set; }

[Required(ErrorMessage = "請選擇是否參加")]
public bool? WillAttend { get; set; }   //是否參加(可空類型)

這是Data Annotation標注的形式來驗證的,如果還不知道Data Annotation為何物,建議先去了解一下
注:需要引用命名空間System.ComponentModel.DataAnnotations;

第二去控制器里判斷是否通過驗證:

if (ModelState.IsValid)
{
    return View("Thanks", guestResponse);
}
else
{
    return View();
}

直接調用的ModelState.IsValid方法來驗證模型是否通過驗證。當然到這里還不算結束,第三去View里應用驗證:
在using語句下面加上:@Html.ValidationSummary() 表示此表單需要驗證,這時候如果表單數據不合法則不會被導向到Thanks視圖。

到此,再跑程序生成的html頁面就又不同了,會有驗證信息在里面。一旦用戶輸入的內容不符合在模型類里定義的規則,那么驗證信息就會顯示出來。看源碼:

<form action="/Home/RsvpForm" method="post">
<
div class="validation-summary-valid" data-valmsg-summary="true">
<
ul><li style="display:none"></li></ul>
</
div>
     <p> Your name: <input data-val="true" data-val-required="姓名不能為空" id="Name" name="Name" type="text" value="" /> </p> <p> Your email: <input data-val="true" data-val-regex="請輸入合法的郵箱地址" data-val-regex-pattern=".+\@.+\..+" data-val-required="郵箱地址不能為空" id="Email" name="Email" type="text" value="" /></p> <p> Your phone: <input data-val="true" data-val-required="電話不能為空" id="Phone" name="Phone" type="text" value="" /></p> <p> Will you attend? <select data-val="true" data-val-required="請選擇是否參加" id="WillAttend" name="WillAttend">
      <
option value="">請選擇</option>       <option value="True">我會參加Party</option>       <option value="False">我不參加Party</option>      </select> </p> <input type="submit" value="提交" /> </form>

高亮錯誤信息行其實很簡單,直接在RsvpForm視圖里添加一個默認css的引用:

<link rel="Stylesheet" href="@Href("~/Content/Site.css")" type="text/css"/>

再提交非法表單信息就高亮了錯誤行:

其實提交表單的一剎那瀏覽器還是有刷新的,說明這是個服務器端驗證,實際項目中更應該服務端和客戶端雙驗證,后續文章會有詳細介紹。

本文源碼

系列文章導航


免責聲明!

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



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