7 天学习 MVC 项目 - 第 5 天






4.92/5 (96投票s)
本文是“7天学习MVC项目”的第5天的延续。
引言
欢迎来到“7天学习MVC项目”系列的第5天。希望您在阅读第1天到第4天时过得很愉快。在学习第5天之前,必须完成前几天的内容。
完整系列
我们很高兴地宣布,本文现已出版成实体书,您可以在 www.amazon.com 和 www.flipkart.com 购买。
议程
- 实验 22 - 添加页脚
- 关于实验 22 的讨论
- 实验 23 – 实现基于角色的安全
- 第一部分
- 第二部分
- 关于实验 23 的讨论
- 实验 24 – 练习题 – 处理 CSRF 攻击
- 实验 25 – 在整个项目中实现一致的外观
- 关于实验 25 的讨论
- 实验 26 – 使用 Action Filter 使页眉和页脚数据代码更高效
- 结论
实验 22 - 添加页脚
在此实验中,我们将为员工屏幕添加页脚。
此实验的主要目标是理解部分视图。
什么是“部分视图”?
从逻辑上讲,部分视图是一个可重用的视图,它永远不会直接显示。它将被包含在其他视图中,并作为该视图的一部分显示。它类似于 Asp.Net Web Forms 中的用户控件,但没有代码隐藏。
步骤 1 – 为部分视图创建 ViewModel
右键单击 ViewModel 文件夹,创建一个名为 FooterViewModel 的类,如下所示。
public class FooterViewModel
{
public string CompanyName { get; set; }
public string Year { get; set; }
}
步骤 2 – 创建部分视图
右键单击“~/Views/Shared”文件夹。选择“添加”>>“视图”。
将视图名称设置为 Footer,选中“创建为部分视图”复选框,然后单击“添加”。
注意:我们已经在第 1 天讨论过共享文件夹。共享文件夹包含不特定于某个控制器的视图。共享文件夹内的视图将可供所有控制器使用。
步骤 3 – 在部分视图中显示数据
打开 Footer.cshtml 并在此处添加以下 HTML。
@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>
步骤 3 – 将页脚数据包含在主 ViewModel 中
打开 EmployeeListViewModel 类,并添加一个新的属性来保存页脚数据,如下所示。
public class EmployeeListViewModel
{
public List<EmployeeViewModel> Employees { get; set; }
public string UserName { get; set; }
public FooterViewModel FooterData { get; set; }//New Property
}
在我们的示例中,页脚(部分视图)将作为 Index 视图的一部分显示。
我们将从 Index 视图向页脚传递必要的数据。
Index 视图是 EmployeeListViewModel 类型的强类型视图,因此页脚视图所需的所有数据都应包含在 EmployeeListViewModel 中。
步骤 4 – 设置页脚数据
打开 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);
}
步骤 5 – 显示页脚
打开 Index.cshtml,然后在 table 标签后添加以下代码以显示页脚部分视图。
</table>
@{
Html.RenderPartial("Footer", Model.FooterData);
}
</div>
</body>
</html>
步骤 6 – 执行和测试
按 F5。导航到 Index 视图。(我相信您现在知道如何操作了。)
关于实验 22 的讨论
Html.Partial 的作用是什么?
与 Html.RenderPartial 类似,Html.Partial 将用于在视图中显示部分视图。
这是语法
@Html.Partial("Footer", Model.FooterData);
语法比之前的简单得多。
两者有什么区别?
Html.RenderPartial 将部分视图的结果直接写入 HTTP 响应流,而 Html.Partial 将结果作为 MvcHtmlString 返回。
什么是 MvcHtmlString,为什么 Html.Partial 返回 MvcHtmlString 而不是 string?
首先,让我们理解 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 而不是 string?
我们已经理解了一个事实:“Razor 总是会编码字符串,但它从不编码 MvcHtmlString”。如果将部分视图的内容视为纯字符串并按原样显示,那将没有意义。我们希望它被视为 HTML 内容,为此我们必须阻止 Razor 进行编码,因此 Partial 方法设计为返回 MvcHtmlString。
推荐使用 Html.RenderPartial 还是 Html.Partial?
推荐使用 Html.RenderPartial,因为它速度更快。
何时会选择 Html.Partial?
当我们需要在显示部分视图返回的结果之前对其进行修改时,推荐使用它。
打开 Index.cshtml 并打开页脚代码,输入以下代码并进行测试。
@{
MvcHtmlString result = Html.Partial ("Footer", Model.FooterData);
string finalResult = result.ToHtmlString().Replace("2015", "20000");
}
@MvcHtmlString.Create(finalResult)
现在页脚看起来如下。
为什么部分视图放在共享文件夹内?
部分视图旨在实现重用,因此它们最适合放在共享文件夹中。
不能将部分视图放在特定的控制器文件夹中,例如 Employee 或 Authentication 吗?
可以这样做,但那样它将仅限于特定控制器。
示例:当我们将部分视图放在 Employee 文件夹中时,它将无法供 AuthenticationController 或与 AuthenticationController 相关的视图使用。
为什么部分视图的定义包含“逻辑上”一词?
在定义中,我们说过部分视图是可重用的视图,但它不会自行执行。它必须放在某个其他视图中,然后作为该视图的一部分显示。
我们关于重用性的说法是完全正确的,但关于执行的说法只是逻辑上的。技术上说,这并不准确。我们可以创建一个返回 ViewResult 的操作方法,如下所示。
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。它旨在作为某个其他视图的一部分显示。因此,我说“逻辑上没有意义”。
为什么创建部分视图而不是直接将页脚内容放在视图中?
两个优点
- 重用性 – 我们可以将同一部分视图重用于其他视图。
- 代码维护 – 将其放在单独的文件中,便于管理和操作。
为什么页眉没有创建为部分视图?
作为最佳实践,我们也必须为页眉创建部分视图,但为了使初始实验更简单,我们将其保留为内联。
实验 23 – 实现基于角色的安全
在此实验中,我们将实现管理员和非管理员登录功能。
要求很简单。
“非管理员用户将无法创建新员工”。
通过这个实验,我们将了解 MVC 中的另外两个主题。
- Session
- 操作过滤器
让我们开始我们的实验 1
为了简单起见,我们将其分为 2 部分。
第一部分 – 从非管理员用户隐藏添加新链接
步骤 1 – 为 UserStatus 创建枚举
右键单击 Models 文件夹,选择“添加新项”。
从对话框中选择“代码文件”选项。
将名称设置为“UserStatus”并单击“添加”。
“代码文件”选项将创建一个空的 “.cs” 文件。
在其中创建一个名为 UserStatus 的枚举,如下所示。
namespace WebApplication1.Models
{
public enum UserStatus
{
AuthenticatedAdmin,
AuthentucatedUser,
NonAuthenticatedUser
}
}
步骤 2 – 更改业务层功能
删除 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;
}
}
步骤 3 – 更改 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?
Session 是 Asp.Net 的一项功能,在 Asp.Net MVC 中被重用。
我们使用 Session 变量来保存与用户相关的数据。Session 变量的生命周期将是用户的生命周期。它将一直可用,直到当前会话结束。
步骤 4 – 删除现有的添加新链接
从“~/Views/Employee”文件夹打开 Index.cshtml 视图,并完全删除“Add New”超链接。
<!-- Remove following line from Index.cshtml -->
<a href="/Employee/AddNew">Add New</a>
步骤 5 – 创建部分视图
右键单击“~/Views/Employee”文件夹,选择“添加”>>“视图”。将视图名称设置为“AddNewLink”,并确保选中“创建部分视图”复选框。
步骤 6 – 将内容放入部分视图
只需将以下内容放入新创建的部分视图中。
<a href="/Employee/AddNew">Add New</a>
步骤 7 – 创建操作方法
打开 EmployeeController,并创建一个名为“GetAddNewLink”的新操作方法,如下所示。
public ActionResult GetAddNewLink()
{
if (Convert.ToBoolean(Session["IsAdmin"]))
{
return Partial View("AddNewLink");
}
else
{
return new EmptyResult();
}
}
步骤 8 – 显示添加新链接
打开 Index.html,只需在此处添加以下代码。
<a href="/Authentication/Logout">Logout</a>
</div>
<hr />
@{
Html.RenderAction("GetAddNewLink");
}
<div>
<table border="1">
<tr>
Html.RenderAction 执行操作方法并将结果直接写入响应流。
步骤 9 – 执行并测试
按 F5 并执行应用程序。
测试1
测试2
第二部分 – 直接 URL 安全
有了上述逻辑,一件事是肯定的。现在非管理员用户将无法通过超链接导航到 AddNew 操作。
这足够了吗?
不,这还不够。如果非管理员用户直接尝试通过 URL 导航到 AddNew 操作怎么办?
如上例所示,非管理员用户能够访问 AddNew 操作。
为了解决这个问题,我们将使用 MVC ActionFilters。Action Filters 允许我们为操作方法添加一些预处理和后处理逻辑。在此实验中,我们将关注 ActionFilters 的预处理支持,并在接下来的实验中关注后处理功能。
步骤 1 – 设置 Filter。
在项目中创建一个名为 Filters 的新文件夹,并创建一个名为 AdminFilter 的新类。
步骤 2 – 创建 Filter
通过继承 ActionFilterAttribute 类,将简单的 AdminFilter 类升级为 ActionFilter,如下所示。
public class AdminFilter:ActionFilterAttribute
{
}
注意:要使用 ActionFilterAttribute,您需要在顶部添加 using System.Web.Mvc。
步骤 3 – 添加安全验证逻辑
在 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."
};
}
}
步骤 4 – 附加 Filter
将过滤器附加到 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 并执行应用程序。使用非管理员凭据登录,并通过输入 AddNew 操作的 URL 来尝试导航到 AddNew 操作方法。
正如您所见,现在您的操作方法已完全安全。
注意:我们在本次实验中用于实现基于角色的安全的所有策略和逻辑可能不是最佳解决方案。您可能有更好的逻辑来实现这种行为。这只是实现它的方法之一。
关于实验 23 的讨论
可以通过浏览器地址栏调用 GetAddNewLink 吗?
是的,我们在“关于实验 22 的讨论”部分已经讨论过这种行为。
是否可以阻止 GetAddNewLink 的直接执行?
为此,请使用 ChildActionOnly 属性装饰 GetAddNewLink。
[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?
当我们需要在显示操作方法执行返回的结果之前对其进行修改时,推荐使用它。
什么是 ActionFilter?
与 AuthorizationFilter 类似,ActionFilter 是 Asp.Net MVC 中的一种 Filter。它允许我们在操作方法执行之前和之后添加逻辑。
注意:在每个实验之后,我们都在尽力涵盖开发人员可能遇到的每个问题。如果您认为还需要包含更多问题,请随时将您的问题发送至 SukeshMarla@Gmail.com。
实验 24 – 练习题 – 处理 CSRF 攻击
从安全角度来看,我们还必须处理项目的 CSRF 攻击。这个我留给你们。
我建议您阅读这篇文章,并将其实现到我们的 SaveEmployee 操作方法中。
https://codeproject.org.cn/Articles/994759/What-is-CSRF-attack-and-how-can-we-prevent-the-sam
实验 25 – 在整个项目中实现一致的外观
在 ASP.NET 世界中,一致的布局意味着 MasterPage。
ASP.NET MVC 也不例外。在 Razor 中,Master Pages 被称为 Layout Pages。
在我们开始实验之前,让我们先讨论一下我们必须放在 Layout Page 中的所有内容。
- 带有欢迎消息的页眉
- 带有页脚数据的页脚
最大的问题?
页脚和页眉的数据是从 Controller 作为 ViewModel 的一部分传递到视图的。
现在的大问题是,当页眉和页脚移至 Layout Page 后,如何将数据从 View 传递到 Layout Page?
解决方案 – 继承
我们可以简单地遵循面向对象的继承原理。让我们通过一个实验来理解它。
步骤 1 – 创建基类 ViewModel
在 ViewModel 文件夹中创建一个名为 BaseViewModel 的新 ViewModel,如下所示。
public class BaseViewModel
{
public string UserName { get; set; }
public FooterViewModel FooterData { get; set; }//New Property
}
如您所见,BaseViewModel 封装了 Layout Page 所需的一切。
步骤 2 – 准备 EmployeeListViewModel
从 EmployeeListViewModel 类中删除 UserName 和 FooterData 属性,并使其继承自 BaseViewModel。
public class EmployeeListViewModel:BaseViewModel
{
public List<EmployeeViewModel> Employees { get; set; }
}
步骤 3 – 创建 Layout Page
右键单击共享文件夹。选择“添加”>>“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>
步骤 4 – 将 Layout 转换为强类型 Layout
只需在布局页面的顶部添加以下语句,使其成为强类型布局。
@using WebApplication1.ViewModels
@model BaseViewModel
步骤 5 – 设计 Layout Page
在布局页面中,添加页眉、页脚和三个内容部分,如下所示。
<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>
如您所见,我们在布局页面中创建了三个部分:TitleSection、HeaderSection 和 ContentBody。内容页面将使用这些部分来定义相应的内容。
注意:在定义 HeaderSection 时,传递了第二个参数。此参数决定了该部分是可选部分还是必需部分。 False 表示它是可选部分。
步骤 6 – 将 Layout Page 附加到 Index View
打开 Index.cshtml,在顶部您会找到以下代码。
@{
Layout = null;
}
将其更改为以下代码。
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
}
步骤 7 – 设计 Index View
- 从 Index View 中移除页眉和页脚。
- 将 body 标签中的其余内容复制并放在某个地方。
- 现在复制 title 标签的内容。
- 删除视图中的所有 HTML 内容。确保只删除 HTML,不应触及 @model 和 layout 语句。
- 使用先前复制的内容定义 TitleSection 和 Contentbody。
完整的视图将如下所示。
@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>
}
正如您所见,视图中的所有内容都定义在某个部分内。
步骤 8 – 执行和测试
按 F5 并执行应用程序。导航到 Index 操作。
步骤 9 – 将 Layout Page 附加到 CreateEmployee View
打开 Index.cshtml,在顶部您会找到以下代码。
@{
Layout = null;
}
将其更改为以下代码。
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
}
步骤 10 – 设计 CreateEmployee View
遵循与步骤 7 相同的过程,并在 CreateEmployee View 中定义部分。这次会有一个补充:我们还将定义 HeaderSection。
完整的 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>
}
步骤 11 – 执行和测试
按 F5 并执行应用程序,然后尝试通过超链接导航到 AddNew 操作。
Index View 是 EmployeeListViewModel 类型的强类型视图,它是 BaseViewModel 的子类,因此它工作正常。CreateEmployee View 是 CreateEmployeeViewModel 类型的强类型视图,它不是 BaseViewModel 的子类,因此出现了这种错误。
步骤 12 – 准备 CreateEmployeeViewModel
使 CreateEmployeeViewModel 继承自 BaseViewModel,如下所示。
public class CreateEmployeeViewModel:BaseViewModel
{
...
步骤 13 – 执行和测试
再次进行测试。
这个错误看起来比它实际的要复杂得多 J
出现这种错误的原因是,我们没有在 AddNew 操作中初始化 Header 和 FooterData。
步骤 14 – 初始化 Header 和 Footer Data
将 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);
}
步骤 15 – 初始化 SaveEmployee 中的 Header 和 FooterData
在 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();
}
步骤 16 – 执行和测试
按 F5 并执行应用程序。
关于实验 25 的讨论
RenderBody 的作用是什么?
当我们第一次在 Layout 页面创建它时,它有一个类似以下的 razor 语句。
@Html.RenderBody()
让我们了解一下它的作用?
在内容页面中,我们通常定义在 Layout Page 中声明的部分。
但奇怪的是,Razor 允许我们在部分外部定义一些内容。
内容页面中所有非部分的内容都将由 RenderBody 函数呈现。
下图更好地解释了这一点。
.
可以有嵌套布局吗?
是的,可以。我们可以创建一个使用其他布局页面的布局页面。语法将是相同的。
是否需要为每个视图指定布局页面?
您会在 Views 文件夹中找到一个名为 __ViewStart.cshtml 的特殊视图。在此视图中定义的设置将应用于所有 Views。
示例 – 只需在 __ViewStart.cshtml 中添加以下代码,它将为所有其他视图设置布局页面。
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
是否需要将 Header 和 FooterData 代码放入每个操作方法?
不,不是必需的。我们可以使用 Action Filter 消除这种重复。这将是我们的下一个实验。
是否必须在每个子视图中定义所有部分?
是的,如果该部分被声明为必需部分。默认值为 true。
@RenderSection("HeaderSection",false) // Not required
@RenderSection("HeaderSection",true) // required
@RenderSection("HeaderSection") // required
实验 26 – 使用 Action Filter 使页眉和页脚数据代码更高效
在实验 23 中,我们看到了 ActionFilter 的一个优点,现在是第二个了。
步骤 1 – 从操作方法中移除冗余代码
从 Index、AddNew 和 SaveEmployee 方法(在 Employee Controller 中)中删除 Header 和 FooterData 代码。
供参考,页眉代码将如下所示。
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();
步骤 2 – 创建 HeaderFooterFilter
在 Filters 文件夹中创建一个名为 HeaderFooterFilter 的新类,并通过继承 ActionFilterAttribute 类将其升级为 Action filter。
步骤 2 - 升级 ViewModel
在 HeaderFooterFilter 类中重写 OnActionExecuted。在此方法中,获取当前视图模型并附加 Header 和 Footer Data。
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 将用于为操作方法执行添加后处理逻辑。
步骤 4 – 附加 Filter
将 HeaderFooterFilter 附加到 Index、AddNew 和 SaveEmployee 操作方法。
[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 并执行应用程序。
结论
至此,我们完成了第 5 天。第 6 天将是最艰难也是最有趣的一天。敬请期待 J
您的评论、邮件总是激励我们做得更多。请在下方发表您的想法和评论或发送邮件至 SukeshMarla@Gmail.com
在 Facebook、LinkedIn 或 Twitter 上与我们联系,以获取新版本更新。
如需在孟买进行线下技术培训,请访问 StepByStepSchools.Net
如需在线培训,请访问 JustCompile.com 或 www.Sukesh-Marla.com
如果您想从 MVC 5 开始,请从下面的视频“2 天学会 MVC 5”开始。