目錄
0. 前言
歡迎來到第五天的學習。希望第一天到第四天的學習,你都是開心的。
1. Lab 22 — 增加 Footer
在這個實驗中,我們將會向 Employee 頁面添加 Footer。本次實驗的目標是理解分部視圖(Partial Views)。
什么是「Partial Views」?
邏輯上講,分部視圖(Partial Views) 是一個可重用的視圖,它不會被直接顯示。它會被其它視圖所包含,然后作為該視圖的一部分來顯示。它類似於 ASP.NET Web Forms 中的用戶控件,但是沒有后台代碼。
第一步:為 Partial View 創建 ViewModel
右擊 ViewModel 文件夾,然后創建一個類,命名為 FooterViewModel。
public class FooterViewModel
{
public string CompanyName { get; set; }
public string Year { get; set; }
}
第二步:創建 Partial View
右擊「~/Views/Shared」文件夾,選擇 Add -> View。
設置視圖的名稱為 Footer。選中「Create as a partial view」復選框,然后點擊「Add」。
注意:我們已經在第一天的學習中談論了 Shared 文件夾。Shared 文件夾包含了視圖,這些視圖不會屬於一個特定的控制器。在 Shared 文件夾下的視圖適用於所有控制器。
第三步:在 Partial View 中顯示數據
打開 Footer.cshtml,然后放置如下代碼。
@using WebApplication1.ViewModels
@model FooterViewModel
<div style="text-align:right;background-color: silver;color: darkcyan;border: 1px solid gray;margin-top:2px;padding-right:10px;">
@Model.CompanyName © @Model.Year
</div>
第四步:在 Main ViewModel 中包含 Footer 數據
打開 EmployeeListViewModel 類,然后增加一個新的屬性來承載 Footer 數據。
public class EmployeeListViewModel
{
public List<EmployeeViewModel> Employees { get; set; }
public string UserName { get; set; }
public FooterViewModel FooterData { get; set; }//New Property
}
在我們的例子中,Footer 視圖將會作為 Index 視圖的一部分展示。
我們將會在 Index 視圖中向 Footer 傳輸必要數據。
Index 視圖是一個 EmployeeListViewModel 的強類型視圖,因此 Footer 中需要的數據都應該被封裝在 EmployeeListViewModel 類中。
第五步:設置 Footer 數據
打開 EmployeeController,然后在 Index 行為方法中設定 FooterData 屬性值。
public ActionResult Index()
{
...
...
employeeListViewModel.FooterData = new FooterViewModel();
employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
employeeListViewModel.FooterData.Year = DateTime.Now.Year.ToString();
return View("Index", employeeListViewModel);
}
第六步:展示 Footer
打開 Index.cshtml 文件,然后在 Table 標簽后展示 Footer 視圖。
</table>
@{
Html.RenderPartial("Footer", Model.FooterData);
}
</div>
</body>
</html>
第七步:執行並測試
按下 F5。導航到 Index 視圖。
Lab 22 的 Q&A
Html.Partial 是用來做什么的?
它類似於 Html.RenderPartial,Html.Partial 用於在視圖中展示 Partial View。
它的語法如下。非常簡單。
@Html.Partial("Footer", Model.FooterData);
兩者的區別是什么?
Html.RenderPartial 將會把 Partial View 的結果寫入 HTTP 響應流中,而 Html.Partial 將會以 MvcHtmlString 的格式返回結果。
什么是 MvcHtmlString,為什么 Html.Partial 返回的是 MvcHtmlString,而不是字符串?
首先讓我們理解下什么是 MvcHtmlString。
MSDN 的定義是「MvcHtmlString 代表一個 HTML 編碼的字符串,這種字符串不應該再次編碼」。
更好地理解這個定義,請查看下面代碼。
@{
string MyString = "My Simple String";
}
@MyString
它將會產生如下輸出。
正如你所看見的,Razor 展示了所有的內容。可能許多人會以為將輸出加粗的字符串,但是 Razor Html 在展示之前對內容進行了編碼,這就是為什么我們獲得的是純內容,而不是加粗的字符串。
當我們不想用 Razor 編碼時,我們可以使用 MvcHtmlString。MvcHtmlString 是 Razor 的一種表示,即「字符串已經編碼了,不再需要額外編碼」。
例如我們可以看下面的代碼。
@{
string MyString = "My Simple String";
}
@MvcHtmlString.Create(MyString)
它將會產生如下輸出。
為什么 Html.Partial 返回的是 MvcHtmlString,而不是字符串呢?
我們已經理解了「Razor 將會編碼字符串,但是不會對 MvcHtmlString 編碼」這一事實。如果 Partial View 內容被認為是像它展示的那樣的純字符串,便沒有意義。我們希望它被當成是一個 HTML 內容,這樣我們就需要停止 Razor 編碼,因此 Partial 方法被設計為返回 MvcHtmlString。
哪個更加推崇,Html.RenderPartial 還是 Html.Partial ?
Html.RenderPartial 更被推崇,因為它更快。
什么時候運用 Html.Partial 更好?
當我們想在展示之前改變 Partial View 返回的結果,推薦使用 Html.Partial。
打開 Index.cshtml,然后打開 Footer,放置如下代碼。
@{
MvcHtmlString result = Html.Partial ("Footer", Model.FooterData);
string finalResult = result.ToHtmlString().Replace("2015", "20000");
}
@MvcHtmlString.Create(finalResult)
現在頁腳展示如下。
為什么將 Partial View 放置在 Shared 文件夾下?
因為 Partial View 意味着可以重復利用的資源,因此放置它們的地點是 Shared 文件夾下。
我們不能將 Partial View 放置到一個特殊的控制器文件夾內嗎?例如 Employee 或者 Authentication?
我們可以這樣做,但是在這種場景下,它將不會適用於指定控制器。
例如:當我們將 Partial View 放置到 Employee 文件夾下,它將不會適用於 AuthenticationController 或者適用於 AuthenticationController 相關的視圖。
為什么 Partial View 的定義包含「邏輯」詞匯?
在定義中,我們已經知道 Partial View 是一個可重用的視圖,但是它不能通過自己執行。它需要放置到其它視圖中,然后作為這些視圖的一部分來展示。
我們所說的 Partial View 可重用是事實,但是我們提到的執行在邏輯上是事實。技術上而言,這不是一個正確的解釋。我們可以創建一個行為方法,來返回如下的視圖結果。
public ActionResult MyFooter()
{
FooterViewModel FooterData = new FooterViewModel();
FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
FooterData.Year = DateTime.Now.Year.ToString();
return View("Footer", FooterData);
}
它將會展示如下的輸出。
盡管在邏輯上沒有意義,但是技術上是可行的。Footer.cshtml 不會包含正確的結構性 HTML。它意味着作為視圖的一部分來展示。因為我們說「邏輯上是沒有意義的」。
為什么要創建 Partial View,而不是直接在視圖底部添加內容?
這樣做有兩個優勢。
-
可重用性。我們可以將一個 Partial View 運用到多個視圖中去。
-
代碼保留。將其放置為一個分割的文件,使其管理和操縱都非常方便。
為什么在 Partial View 中沒有創建 Header?
作為最佳實踐,我們需要為 Partial View 創建 Header,但是為了保持最初實驗簡單化,我們並沒有這樣做。
2. Lab 23 — 實現基於角色的安全性
在本次實驗中,我們將會實現 Admin 和 Non-Admin 兩種登錄功能。
需求是很簡單的。即「Non-Admin 用戶不能創建 Employees」。
通過這個實驗,我們將會理解 MVC 中的兩個主題。
-
Session
-
Action Filters
現在我們開始進行實驗。為了簡單化,我們將實驗分為兩部分。
Part 1 — Non-Admin 用戶登錄,隱藏 AddNew 鏈接
第一步:創建標識 UserStatus 的枚舉
右擊 Models 文件夾,選擇「Add New Item」。
在對話框中選擇「Code File」選項。
在名稱欄中輸入「UserStatus」,然后點擊添加。「Code File」的選項將會創建一個空白的「.cs」文件。
創建一個枚舉,命名為 UserStatus,代碼如下。
namespace WebApplication1.Models
{
public enum UserStatus
{
AuthenticatedAdmin,
AuthentucatedUser,
NonAuthenticatedUser
}
}
第二步:更改業務層功能
刪除 IsValidUser 功能,然后創建一個新的功能,命名為 GetUserValidity。
public UserStatus GetUserValidity(UserDetails u)
{
if (u.UserName == "Admin" && u.Password == "Admin")
{
return UserStatus.AuthenticatedAdmin;
}
else if (u.UserName == "Sukesh" && u.Password == "Sukesh")
{
return UserStatus.AuthentucatedUser;
}
else
{
return UserStatus.NonAuthenticatedUser;
}
}
第三步:更改 DoLogin 行為方法
打開 AuthenticationController,然后更改 DoLogin 行為方法如下。
[HttpPost]
public ActionResult DoLogin(UserDetails u)
{
if (ModelState.IsValid)
{
EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
//New Code Start
UserStatus status = bal.GetUserValidity(u);
bool IsAdmin = false;
if (status==UserStatus.AuthenticatedAdmin)
{
IsAdmin = true;
}
else if (status == UserStatus.AuthentucatedUser)
{
IsAdmin = false;
}
else
{
ModelState.AddModelError("CredentialError", "Invalid Username or Password");
return View("Login");
}
FormsAuthentication.SetAuthCookie(u.UserName, false);
Session["IsAdmin"] = IsAdmin;
return RedirectToAction("Index", "Employee");
//New Code End
}
else
{
return View("Login");
}
}
正如你所看見的,我們運用 Session 變量來識別用戶是 Admin 用戶還是 non-Admin 用戶。
不知道什么是 Session?
Session 是 ASP.NET 的一個功能,在 ASP.NET MVC 中被重用。
我們運用 Session 變量來承載用戶相關的數據。Session 的生命周期取決於用戶的生命周期。它將一直可用直到當前的 Session 結束。
第四步:刪除已經存在的 AddNew 鏈接
在「~/Views/Employee」文件夾下打開 Index.cshtml 視圖,然后完全刪除「Add New」超鏈接。
<!-- Remove following line from Index.cshtml -->
<a href="/Employee/AddNew">Add New</a>
第五步:創建 Partial View
右擊「~/Views/Employee」文件夾,然后選擇 Add -> View。設置視圖的名稱為「AddNewLink」,然后確保選擇「Create as a partial view」復選框。
第六步:在 Partial View 中放置內容
在剛創建的 Partial View 中放置如下內容。
<a href="/Employee/AddNew">Add New</a>
第七步:創建行為方法
打開 EmployeeController 然后創建一個新的行為方法,命名為「GetAddNewLink」。
public ActionResult GetAddNewLink()
{
if (Convert.ToBoolean(Session["IsAdmin"]))
{
return Partial View("AddNewLink");
}
else
{
return new EmptyResult();
}
}
第八步:展示 AddNew 鏈接
打開 Index.html,然后放置如下代碼。
<a href="/Authentication/Logout">Logout</a>
</div>
<hr />
@{
Html.RenderAction("GetAddNewLink");
}
<div>
<table border="1">
<tr>
Html.RenderAction 執行行為方法,然后向響應流中直接寫入結果。
第九步:執行並測試
按下 F5,然后執行應用。
- Test 1
- Test 2
Part 2 — 直接的 URL 安全性
按照上述的邏輯,一件事是可以確保的。即現在 non-Admin 用戶不能通過超鏈接導航到 AddNew 行為。
這樣就夠了嗎?
答案是否定的,這還不夠。如果一個 non-Admin 用戶直接通過 URL 試圖導航到 AddNew 行為會發生什么呢。
正如你在上述例子中所看見的,一個 non-Admin 用戶依然可以訪問 AddNew 行為。
為了解決這個問題,我們需要運用 MVC 中的 Action Filters。Action Filters 讓我們向行為方法中添加一些預處理和后處理的邏輯。在本實驗中,我們將着重於 Action Filters 的預處理功能,在后面的實驗中,我們再着重於后處理功能。
第一步:設置過濾器
在項目下創建一個新的文件夾,命名為 Filters,然后創建一個新的類,命名為 AdminFilter。
第二步:創建過濾器
升級簡單的 AdminFilter 類到 ActionFilter,通過將其繼承 ActionFilterAttribute 類,代碼如下。
public class AdminFilter:ActionFilterAttribute
{
}
注:為了運用 ActionFilterAttribute,你需要在頂部引用 System.Web.Mvc。
第三步:增加安全認證邏輯
在 ActionFilter 中重寫 OnActionExecuting 方法。
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!Convert.ToBoolean(filterContext.HttpContext.Session["IsAdmin"]))
{
filterContext.Result = new ContentResult()
{
Content="Unauthorized to access specified resource."
};
}
}
第四步:附加過濾器
向 AddNew 和 SaveEmployee 行為方法添加過濾器。
[AdminFilter]
public ActionResult AddNew()
{
return View("CreateEmployee",new Employee());
}
...
...
[AdminFilter]
public ActionResult SaveEmployee(Employee e, string BtnSubmit)
{
switch (BtnSubmit)
{
case "Save Employee":
if (ModelState.IsValid)
{
EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
....
....
第五步:執行和測試
按下 F5,然后執行應用。運用 non-Admin 身份登錄,然后試圖通過 AddNew 行為的 URL 導航到 AddNew 行為方法。
正如你所看見的,現在你的行為方法處於完全安全狀態。
Lab 23 的 Q&A
我們可以通過地址欄直接觸發 GetAddNewLink 嗎?
答案是肯定的,我們已經在「Talk on Lab 22」部分討論了這個行為。
直接停止執行 GetAddNewLink 有可能嗎?
可以直接在 GetAddNewLink 中添加 ChildActionOnly 屬性。
[ChildActionOnly]
public ActionResult GetAddNewLink()
{
if (Convert.ToBoolean(Session["IsAdmin"]))
{
Html.Action 是用來做什么的?
就類似於 Html.RenderAction,Html.Action 將會執行行為方法用於呈現視圖的結果。下面是語法。
@Html.Action("GetAddNewLink");
可以看出,語法相對來說簡單多了。
兩者的區別是什么?
Html.RenderAction 將會把行為方法的執行結果直接寫入 HTTP 響應流,而 Html.Action 將會返回 MvcHtmlString 結果。
哪個更推崇?是 Html.RenderAction 還是 Html.Action?
更推崇 Html.RenderAction,因為它更快。
什么時候用 Html.Action 更好?
當我們想在呈現之前改變行為方法執行的結果時,用 Html.Action 更好。
什么是 ActionFilter?
就類似於 AuthenticationFilter,ActionFilter 是 ASP.NET MVC 的一種過濾器類型。它允許我們向行為方法添加預處理和后處理邏輯。
3. Lab 24 — 任務實驗 — 處理 CSRF 攻擊
從視圖的安全性方面出發,我們還需要在項目中處理 CSRF 攻擊。這里我將不再做過多指導,你需要自己手動完成。
我建議你閱讀下述文章,然后實現方法。
4. Lab 25 — 實現項目的一致性外觀
在 ASP.NET 領域中,一致性的布局意味着母版頁(MasterPage)。
但 ASP.NET MVC 是區別於此的。在 Razor 中,母版頁被稱為布局頁(Layout Pages)。
在正式開始試驗之前,我們先來討論一下在母版頁中我們需要放置哪些元素。
1.帶有歡迎信息的 Header。
2.帶有頁腳數據的 Footer。
最大的問題是什么?
頁腳和頁眉的數據作為 ViewModel 的一部分從控制器傳輸到視圖中。
現在最大的問題便是,當頁眉和頁腳移動到布局頁后,數據如何從視圖傳輸到布局頁。
解決方案 — 繼承
在這里我們可以簡單地遵循面向對象繼承准則。讓我們通過一個小實驗來理解。
第一步:創建 ViewModel 的基類
在 ViewModel 文件夾下創建一個新的 ViewModel 類,稱為 BaseViewModel 類。
public class BaseViewModel
{
public string UserName { get; set; }
public FooterViewModel FooterData { get; set; }//New Property
}
正如你所看見的,BaseViewModel 封裝了 Layout 頁所需的所有元素。
第二步:准備 EmployeeListViewModel
從 EmployeeListViewModel 類中移除 UserName 和 FooterData 屬性,然后讓它繼承 BaseViewModel 類。
public class EmployeeListViewModel:BaseViewModel
{
public List<EmployeeViewModel> Employees { get; set; }
}
第三步:創建布局頁
右擊 Shared 文件夾,選擇 Add -> MVC 5 Layout Page。輸入名稱為 MyLayout,然后點擊確定。
它將會創建一個如下格式的代碼。
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
</head>
<body>
<div>
@RenderBody()
</div>
</body>
</html>
第四步:轉換布局頁為強類型布局
在布局頁的上方放置如下的簡單聲明,使其變為強類型布局。
@using WebApplication1.ViewModels
@model BaseViewModel
第五步:設計布局頁
在布局頁添加頁眉,頁腳和內容三部分。
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@RenderSection("TitleSection")</title>
@RenderSection("HeaderSection",false)
</head>
<body>
<div style="text-align:right">
Hello, @Model.UserName
<a href="/Authentication/Logout">Logout</a>
</div>
<hr />
<div>
@RenderSection("ContentBody")
</div>
@Html.Partial("Footer",Model.FooterData)
</body>
</html>
正如你所看見的,我們已經為布局頁創建了三塊。Title 部分,Header 部分和Content 部分。內容頁面將會用到這三部分來定義合適的內容。
第六步:向 Index 視圖附上 Layout 頁面
打開 Index.cshtml 頁面,在頂部會發現如下代碼。
@{
Layout = null;
}
將這段代碼改為如下代碼。
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
}
第七步:設計 Index 視圖
- 從 Index 視圖中移除 Headers 和 Footers。
- 復制 Body 標簽中的剩余內容,然后將它保存在別處。
- 復制 Title 標簽里的內容。
- 將視圖中的所有 HTML 內容都移除。確保你只是移除了 HTML,@model 和布局聲明不需要被移除。
- 定義 Title 部分和 Content 部分,內容是剛才所復制下的內容。
完整的視圖將會如下所示。
@using WebApplication1.ViewModels
@model EmployeeListViewModel
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
}
@section TitleSection{
MyView
}
@section ContentBody{
<div>
@{
Html.RenderAction("GetAddNewLink");
}
<table border="1">
<tr>
<th>Employee Name</th>
<th>Salary</th>
</tr>
@foreach (EmployeeViewModel item in Model.Employees)
{
<tr>
<td>@item.EmployeeName</td>
<td style="background-color:@item.SalaryColor">@item.Salary</td>
</tr>
}
</table>
</div>
}
正如你所看見的,視圖中所有的元素都定義在指定的位置上。
第八步:執行並測試
按下 F5,然后執行應用。導航到 Index 行為上。
第九步:在 CreateEmployee 視圖中附上 Layout 頁面
打開 Index.cshtml 頁面,在頂部會發現如下代碼。
@{
Layout = null;
}
將其改為如下代碼。
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
}
第十步:設計 CreateEmployee 視圖
像第七步的步驟一樣,定義 CreateEmployee 視圖的區域。這一次會增加一點。我們將會定義 Header 部分。
完整的 HTML 代碼如下。
@using WebApplication1.Models
@model Employee
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
}
@section TitleSection{
CreateEmployee
}
@section HeaderSection{
<script src="~/Scripts/Validations.js"></script>
<script>
function ResetForm() {
document.getElementById('TxtFName').value = "";
document.getElementById('TxtLName').value = "";
document.getElementById('TxtSalary').value = "";
}
</script>
}
@section ContentBody{
<div>
<form action="/Employee/SaveEmployee" method="post" id="EmployeeForm">
<table>
<tr>
<td>
First Name:
</td>
<td>
<input type="text" id="TxtFName" name="FirstName" value="@Model.FirstName" />
</td>
</tr>
<tr>
<td colspan="2" align="right">
@Html.ValidationMessage("FirstName")
</td>
</tr>
<tr>
<td>
Last Name:
</td>
<td>
<input type="text" id="TxtLName" name="LastName" value="@Model.LastName" />
</td>
</tr>
<tr>
<td colspan="2" align="right">
@Html.ValidationMessage("LastName")
</td>
</tr>
<tr>
<td>
Salary:
</td>
<td>
<input type="text" id="TxtSalary" name="Salary" value="@Model.Salary" />
</td>
</tr>
<tr>
<td colspan="2" align="right">
@Html.ValidationMessage("Salary")
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="BtnSubmit" value="Save Employee" onclick="return IsValid();" />
<input type="submit" name="BtnSubmit" value="Cancel" />
<input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" />
</td>
</tr>
</table>
</div>
}
第十一步:執行並測試
按下 F5,然后執行應用。通過嘗試超鏈接來導航到 AddNew 行為上。
Index 視圖是 EmployeeListViewModel 的強類型視圖,EmployeeListViewModel 又是 BaseViewModel 的子類,所以 Index 視圖可以運轉。但是 CreateEmployee 視圖是 CreateEmployeeViewModel 的強類型視圖,而 CreateEmployeeViewModel 不是 BaseViewModel 的子類,所以 CreateEmployee 出現了這樣的錯誤。
第十二步:准備 CreateEmployeeViewModel
讓 CreateEmployeeViewModel 繼承 BaseViewModel,代碼如下。
public class CreateEmployeeViewModel:BaseViewModel
{
...
第十三步:執行並測試
再一次測試。
這次的錯誤看起來與之前的不一樣。錯誤真正的原因是,我們在 AddNew 行為中沒有初始化 Header 和 Footer 的數據。
第十四步:初始化 Header 和 Footer 數據
將 AddNew 行為方法的代碼改為如下所示。
public ActionResult AddNew()
{
CreateEmployeeViewModel employeeListViewModel = new CreateEmployeeViewModel();
employeeListViewModel.FooterData = new FooterViewModel();
employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
employeeListViewModel.FooterData.Year = DateTime.Now.Year.ToString();
employeeListViewModel.UserName = User.Identity.Name; //New Line
return View("CreateEmployee", employeeListViewModel);
}
第十五步:在 SaveEmployee 中初始化 Header 和 Footer 數據
類似於 SaveEmployee 行為方法一樣,我們更改其代碼如下。
public ActionResult SaveEmployee(Employee e, string BtnSubmit)
{
switch (BtnSubmit)
{
case "Save Employee":
if (ModelState.IsValid)
{
...
}
else
{
CreateEmployeeViewModel vm = new CreateEmployeeViewModel();
...
vm.FooterData = new FooterViewModel();
vm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
vm.FooterData.Year = DateTime.Now.Year.ToString();
vm.UserName = User.Identity.Name; //New Line
return View("CreateEmployee", vm); // Day 4 Change - Passing e here
}
case "Cancel":
return RedirectToAction("Index");
}
return new EmptyResult();
}
第十六步:執行並測試
按下 F5,然后執行應用。
Lab 25 的 Q&A
RenderBody 是用於做什么的?
當我們第一次創建 Layout 頁面時,我們有一個 Razor 聲明如下。
@Html.RenderBody()
現在讓我們來理解下它是做什么的。在內容頁面上,我們正常地定義區域,這些區域在布局頁聲明。
但是奇怪的是,Razor 允許我們在區域外定義一些內容。在內容頁面上,所有非區域內的內容將會被 RenderBody 函數呈現。
下圖將會更好地進行解釋。
我們有嵌套的布局嗎?
答案是肯定的。我們能創建一個嵌套了其它布局頁的布局頁。語法是一樣的。
在每一個視圖中都指定布局頁是必須的嗎?
你能夠在 Views 文件夾下發現一個特殊的布局頁,稱為「__ ViewStart.cshtml」。在其內部設定定義,將會應用於所有視圖。
例如,在「__ ViewStart.cshtml」中放置如下的代碼,將會使「_Layout.cshtml」適用於所有視圖的布局。
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
在每一個行為方法中,是否都需要放置 Header 和 Footer 的數據代碼?
答案是否定的。我們可以運用 Action 過濾器來避免這種重復。我們將會在接下來的實驗中實踐。
在子視圖中定義所有區域是否是必須的?
答案是肯定的。如果 Section 的聲明為必須的,那么默認值是 True。
@RenderSection("HeaderSection",false) // Not required
@RenderSection("HeaderSection",true) // required
@RenderSection("HeaderSection") // required
5. Lab 26 — 運用 Action 過濾器讓 Header 和 Footer 數據更高效
在 Lab 23 中,我們已經知道了 ActionFilter 的優勢,現在我們來看它的第二點優勢。
第一步:從行為方法中刪除冗余代碼
從 Index,AddNew 和 SaveEmployee 方法中刪除 Header 和 Footer 數據的代碼。
Header 代碼如下。
bvm.UserName = HttpContext.Current.User.Identity.Name;
Footer 代碼如下。
bvm.FooterData = new FooterViewModel();
bvm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
bvm.FooterData.Year = DateTime.Now.Year.ToString();
第二步:創建 HeaderFooterFilter
在 Filters 文件夾下創建一個類,命名為 HeaderFooterFilter,然后通過將它繼承 ActionFilterAttribute 類來將其升級 Action 過濾器。
第三步:升級 ViewModel
在 HeaderFooterFilter 類中重寫 OnActionExecuted。在這個方法中我們將會得到當前的視圖模型,然后將其附上 Header 和 Footer 數據。
public class HeaderFooterFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ViewResult v = filterContext.Result as ViewResult;
if(v!=null) // v will null when v is not a ViewResult
{
BaseViewModel bvm = v.Model as BaseViewModel;
if(bvm!=null)//bvm will be null when we want a view without Header and footer
{
bvm.UserName = HttpContext.Current.User.Identity.Name;
bvm.FooterData = new FooterViewModel();
bvm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
bvm.FooterData.Year = DateTime.Now.Year.ToString();
}
}
}
}
OnActionExecuted 方法用於添加行為方法執行的邏輯操作。
第四步:附上過濾器
在 Index,AddNew 和 SaveEmployee 方法中附上 HeaderFooterFilter。
[HeaderFooterFilter]
public ActionResult Index()
{
EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
...
}
...
[AdminFilter]
[HeaderFooterFilter]
public ActionResult AddNew()
{
CreateEmployeeViewModel employeeListViewModel = new CreateEmployeeViewModel();
//employeeListViewModel.FooterData = new FooterViewModel();
//employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";
...
}
...
[AdminFilter]
[HeaderFooterFilter]
public ActionResult SaveEmployee(Employee e, string BtnSubmit)
{
switch (BtnSubmit)
{
...
第五步:執行並測試
按下 F5,執行應用。
6. 總結
這里我們已經完成了第五天的學習。接下來的第六天學習是最困難的,也是最有意思的。
繼續保持學習的熱情吧!