基於 jQuery 的專業 ASP.NET WebForms/MVC 控件庫!
目錄
【第一篇】ASP.NET MVC快速入門之數據庫操作(MVC5+EF6)
【第二篇】ASP.NET MVC快速入門之數據註解(MVC5+EF6)
【第三篇】ASP.NET MVC快速入門之安全策略(MVC5+EF6)
【第四篇】ASP.NET MVC快速入門之完整示例(MVC5+EF6)
【番外篇】ASP.NET MVC快速入門之免費jQuery控件庫(MVC5+EF6)
請關註三石的博客:http://cnblogs.com/sanshi
新建項目
打開VS2015,找到菜單項[文件->新建->項目],打開向導對話框:
註意我們的選擇項:
- 運行平臺:.NET FrameWork 4.5
- 項目模板:ASP.NET Web Application (.NET Framework)
- 項目名稱:AspNetMvc.QuickStart,如果你在跟著本教程練習,建議起相同的項目名稱,方便直接拷貝代碼到你的項目中。
點擊[確定]按鈕,向導會帶我們到另一個選擇對話框:
由於本教程是快速入門,所以我們從最簡單的入手,只勾選必需的選項:
- 不進行身份驗證。ASP.NET MVC提供了完善的身份驗證方案,我們會有單獨的文章講解。
- 僅勾選 MVC 引用。
點擊[確定],VS2015會創建一個可直接運行的項目,按下快捷鍵[Ctrl+F5],不調試直接運行:
默認的目錄結構如下:
如果你之前在WebForms下進行開發,對其中的一些文件夾和文件應該很熟悉了:
- Web.config:項目配置文件,裏面保存項目配置參數以及數據庫連接字符串。
- packages.config:Nuget配置文件
- Global.asax:全局代碼文件,提供應用程序級別以及會話級別的事件處理函數,可以在Application_Start中註冊全局變量。
- favicon.ico:瀏覽器地址欄圖標,在HTML的head標簽中引用。
- App_Data:放置本地數據庫文件,比如LocalDB生成的數據庫文件。
下面幾個文件夾,用來放置靜態文件,從名稱就可以方便的猜出其用途:
- Scripts:放置靜態腳本文件,比如jQuery等。
- fonts:放置圖標字體文件,比如流行的FontAwesome字體等。
- Content:放置靜態文件,比如xml文件、Bootstrap的CSS庫。
下面幾個文件是ASP.NET MVC新引入的:
- App_Start:用來放置應用初始化類,這個是MVC4引入的一個命名約定,其實這就是一個普通的文件夾,沒有特殊的含義。
- Controllers:控制器類。
- Models:模型類,比如EF CodeFirst的模型定義。
- Views:視圖文件,最初的視圖引擎是WebForms View Engine,使用和ASPX文件相同的語法,而現在用的Razor視圖引擎是MVC3引入的,以cshtml為後綴。
頁面流程
首先看下 [About]頁面:
這個頁面之所以能夠呈現在我們眼前,經歷了三個主要流程:
- MVC的路由引擎根據URL查找相應的控制器(HomeController.cs)。
- 控制器的操作方法About準備數據,然後傳入視圖Home/About.cshtml。
- 視圖準備HTML片段,放入布局頁面並返回瀏覽器。
路由引擎->控制器
一切還得從Global.asax中說起,在其中的應用程序啟用事件中,我們需要註冊路由處理器:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); }
RouteConfig.cs類位於App_Start文件夾中,我們來看下內容:
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
這裏註冊一個名為Default的路由規則,對應的URL是{controller}/{action}/{id},這裏三個占位符分別表示:
- {controller}:控制器,默認是Home,對應的控制器類是HomeController.cs。
- {action}:控制器裏面的方法,默認是Index。所以如果用戶直接通過http://localhost/訪問系統時,默認調用Home控制器中的Index方法處理。
- {id}:參數ID,可選項,這個參數對應於操作方法中的id參數。
控制器方法->視圖
通過上面的介紹,我們就知道了http://localhost:55654/Home/About網址對應於Home控制器的About方法。
我們在Controllers/HomeController.cs中找到相應的方法:
public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); }
ViewBag是一個動態對象(dynamic),可以用來存儲任意參數,用來從控制器向視圖傳遞數據。
從控制器向視圖傳遞數據一般有兩種方法:
- 傳入模型,然後在視圖中通過Model對象訪問,這是一種強類型的方式,也是推薦的做法。其局限性就是只能傳入一個模型,如果需要傳入多個模型對象,就需要自定義類來包含多個模型,另一種方法就是ViewBag。
- ViewBag,視圖包傳遞數據非常方便,但是在視圖中可能需要進行強制類型轉換。在常見的傳入一個主模型和多個次模型時,可以把多次模型放到ViewBag中,從而避免自定義類的麻煩。
作為命名約定,這個操作方法會自動調用相應名稱的視圖文件About.cshtml。
視圖->瀏覽器
下面來看About.cshtml視圖文件:
@{ ViewBag.Title = "About"; } <h2>@ViewBag.Title.</h2> <h3>@ViewBag.Message</h3> <p>Use this area to provide additional information.</p>
以@開頭用來輸出C#代碼的運行結果,MVC會自動判斷於何處結束C#代碼,並轉入HTML代碼。
需要註意,頁面第一行的@{ }用來執行一段C#代碼,不會輸出內容,這裏定義了一個ViewBag.Title的變量,並在下面的代碼中使用@ViewBag.Title輸出到頁面中。
很多初學者可能有些疑惑,為啥控制器中定義了ViewBag.Message,而在視圖中定義了ViewBag.Title,這兩者有啥區別?
一般來說是沒有功能的區別,僅僅是語義的區別。在視圖中定義的變量僅在視圖中使用,比如這裏定義的ViewBag.Title不僅在About.cshtml中使用,而且在布局視圖Shared/-_Layout.cshtml中也用到了。
布局視圖
布局視圖類似於WebForms中的母版頁,具體的視圖頁面會作為一部分嵌入到布局視圖中,然後返回到瀏覽器形成一個完整的頁面。
每一個視圖頁面默認會使用Views/_ViewStart.cshtml中的定義的內容:
@{ Layout = "~/Views/Shared/_Layout.cshtml"; }
這裏面指定了布局視圖的位置,我們來簡單看下布局視圖的內容:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - My ASP.NET Application</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home")</li> </ul> </div> </div> </div> <div class="container body-content"> @RenderBody() <hr /> <footer> <p>© @DateTime.Now.Year - My ASP.NET Application</p> </footer> </div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap") @RenderSection("scripts", required: false) </body> </html>
其中head標簽下面的title中使用了在About視圖中定義的ViewBag.Title屬性。
這個布局視圖使用Bootstrap庫定義的CSS樣式來完成,包含標題欄,導航菜單,以及頁腳的定義,具體的內容會嵌入@RenderBody()的地方,最終形成完整的HTML頁面返回。
數據庫操作
上面從控制器傳入視圖的數據是硬編碼的一個字符串,實際項目中則經常需要從數據庫中讀取數據,我們使用微軟推薦的Entity Framework CodeFirst開發模式來創建和使用數據庫。
安裝Entity Framework
首先需要安裝EF,在VS2015中找到[工具]菜單,然後找到NuGet包管理器:
轉到[瀏覽]選項卡,可以搜索Entity Framework,安裝其最新穩定版到項目中:
安裝後,會自動更改Web.config添加相應的配置信息。
創建模型
我們計劃完成一個簡單的學生管理系統,包含基本的增刪改查(CRUD)。
首先在Models文件,創建學生(Student)的模型類:
public class Student { public int ID { get; set; } public string Name { get; set; } public int Gender { get; set; } public string Major { get; set; } public DateTime EntranceDate { get; set; } }
然後創建數據庫操作上下文,EF需要這個文件來創建和訪問數據庫:
public class StudentDbContext : DbContext { public DbSet<Student> Students { get; set; } }
由於這個類繼承自EF的DbContext基類,因此需要在文件頭部添加如下引用:
using System.Data.Entity;
創建完這兩個文件,需要重新編譯項目(快捷鍵Ctrl+Shift+B),否則下面添加控制器時會出錯。
添加控制器
在Controllers目錄上點擊右鍵,添加控制器,彈出向導對話框:
這裏選擇 MVC 5 Controller with views, using Entity Framework,然後進入設置對話框:
在這個對話框中,我們需要指定剛才創建的模型類(Student)和數據訪問上下文類(StudentDbContext),然後VS不僅可以自動創建視圖,而且使用EF自動創建CRUD的全部代碼,是不是很酷!
全部功能完成了!
是不是很驚奇,我們甚至沒來得及寫視圖代碼,沒有配置數據庫,沒有寫CRUD的邏輯代碼,VS模板幫我們生成了一切,現在運行一下(Ctrl+F5),並在瀏覽器中輸入/Students:
表格頁面
表格頁面對應於Students控制器下的Index操作方法:
public class StudentsController : Controller { private StudentDbContext db = new StudentDbContext(); // GET: Students public ActionResult Index() { return View(db.Students.ToList()); } }
首先,我們看到控制器內部定義了一個私有變量db,並進行初始化。這是數據庫操作上下文實例,所有的CRUD操作都講依賴於這個實例。
在Index方法中,通過向View方法傳遞學生列表的方式,把模型數據傳遞到了視圖,在Views/Students/Index.cshtml視圖文件中,我們聲明了傳入模型的類型:
@model IEnumerable<AspNetMvc.QuickStart.Models.Student>
在視圖中,Model屬性的類型就確定為強類型IEnumrable<Student>,配合VS提供的智能感知,不僅可以快速編寫代碼,並且在編譯時還檢查代碼的有效性。
完整的Index.cshtml代碼:
@model IEnumerable<AspNetMvc.QuickStart.Models.Student> @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table class="table"> <tr> <th> @Html.DisplayNameFor(model => model.Name) </th> <th> @Html.DisplayNameFor(model => model.Gender) </th> <th> @Html.DisplayNameFor(model => model.Major) </th> <th> @Html.DisplayNameFor(model => model.EntranceDate) </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.DisplayFor(modelItem => item.Gender) </td> <td> @Html.DisplayFor(modelItem => item.Major) </td> <td> @Html.DisplayFor(modelItem => item.EntranceDate) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | @Html.ActionLink("Details", "Details", new { id=item.ID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ID }) </td> </tr> } </table>
看著很有古老的ASP的感覺吧,不過這裏的Model屬性是強類型的,因此在foreach循環中,VS明確知道item類型是Student,從而方便代碼編寫:
@Html裏面都是MVC提供的輔助方法,用來輔助生成HTML代碼:
- ActionLink:用來生成超鏈接,鏈接到本控制器內的某個操作方法(也可以是其他控制器的方法,有重載函數),可以指定路由參數,通過對象初始化語法來創建,比如new {id=item.ID}。
- DisplayNameFor:顯示模型屬性的名稱。強類型輔助方法,允許我們使用一個lambda表達式來指定某個模型屬性,而不用寫字符串。好處不僅有智能感知,編譯時檢查,而且也方便代碼重構,比如我們在更改模型的屬性名稱時,視圖中的相應代碼也會改變。
- DisplayFor:顯示模型屬性的值。
新增頁面
新增頁面對應於Students控制器下的Create操作方法:
// GET: Students/Create public ActionResult Create() { return View(); }
對應的視圖文件:
@model AspNetMvc.QuickStart.Models.Student @{ ViewBag.Title = "Create"; } <h2>Create</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Student</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" }) </div> </div> @* 省略 Gender Major EntranceDate *@ <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
首先定義了視圖中使用的模型類型是Student,這樣LabelFor強類型輔助方法就可以從模型元數據中獲取需要顯示的文本。
頁面打開時,由於並未傳入任何模型對象,所以Model為空對象,如下所示:
所以頁面上默認的輸入框都是空的,截圖中是作者輸入值後的效果。
Html.BeginForm()會在頁面上生成一個form標簽,默認的提交地址還是當前頁面(action=/Students/Create),默認的請求方法是post,如下所示:
因此,點擊[Create]按鈕時,會發出一個POST請求到後臺,對應於Students控制器的Create方法。
保存數據與模型綁定
下面我們來看下擁有[HttpPost]元數據的Create方法:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Create([Bind(Include = "ID,Name,Gender,Major,EntranceDate")] Student student) { if (ModelState.IsValid) { db.Students.Add(student); db.SaveChanges(); return RedirectToAction("Index"); } return View(student); }
這裏面有兩個安全措施:
- ValidateAntiForgeryToken:用來阻止CSRF(跨站請求偽造)。
- Bind:用來阻止Over-Posting(過多提交攻擊)。
這兩個安全手段我們會在以後的文章中詳細介紹,這裏就先略過。
我們先看下本次請求的POST參數:
但是Create方法中只有一個Student對象參數,是不是很神奇,其實這是一個重要的概念模型綁定。
如果在WebForms中,我們可以會寫一堆代碼來從Request.Form中獲取參數,並重建Student對象,類似如下代碼:
Student student = new Student(); student.Name = Request.Form["Name"]; student.Gender = Convert.ToInt32(Request.Form["Gender"]); ....
在MVC中,這一過程是自動完成,簡單來說這就是模型綁定。
但是實際的模型綁定過程,不僅在請求的表單數據中查找,還會在路由參數,URL查詢字符串,以及Cookie中查找。
如果模型綁定失敗(比如模型參數不符合驗證規則),則ModelState.IsValid就為false,這時會直接返回頁面內容,此時模型對象student中保存的是用戶輸入的值,前端也會有錯誤提示,這個過程我們會在下一篇文章中講解。
如果模型綁定成功,則保存新增數據,然後通過RedirectToAction來重定向到表格頁面:
小結
這篇文章首先介紹了VS2015下MVC項目的創建過程;然後簡要概述頁面執行的流程,從路由引擎到控制器,再由控制器到視圖,最後由視圖返回到瀏覽器,而模型是作為控制器傳入視圖的參數,這樣清晰明了;最後使用VS提供的模板,創建了一個帶CRUD操作的數據訪問實例。
EF CodeFirst讓我們的關註點從數據庫轉移到了模型,而模型又是MVC的核心所在,對模型進行恰當的數據註解,不僅會影響數據庫的表結構,而且會影響瀏覽器端的數據驗證和服務端的數據驗證,因此下一篇文章我們會詳細介紹一下數據註解。
下載示例源代碼
基於 jQuery 的專業 ASP.NET WebForms/MVC 控件庫!