7 天学习 MVC 项目 - 奖励日 2






4.97/5 (43投票s)
本文是“7天学会MVC项目”系列文章的续篇。
引言
本文是“7天学会MVC项目”系列文章的续篇。正如我承诺的,这里是该系列的第二篇奖励日文章。这是本系列的最后一篇文章。我相信您已经享受了完整的系列。感谢您的所有支持,祝您好运。
今天我们将讨论一些在项目中尚未涵盖的 Asp.Net MVC 的主题。
完整系列
我们很高兴地宣布,本文现已推出实体书版本,您可以在 www.amazon.com 和 www.flipkart.com 购买。
理解 Asp.Net MVC 中的 TempData
对于这个主题,我们不会做任何演示。TempData 在不同的情况下表现不同。将所有场景都包含在一个项目中是不可能的。因此,我们将逐步尝试涵盖和理解所有场景。
什么是 TempData?
TempData 是一种生命周期较短的 Session。与 Session 类似,它是一种键值对,用于保存用户特定的值。Session 和 TempData 的区别在于,TempData 只能在单个请求周期内维护数据。
要正确理解 TempData,您必须正确理解“单个请求”的含义。
- 用户通过浏览器的地址栏发出一个请求,该请求由一个动作方法处理。我们称之为请求 1。
- 在最终响应到达用户之前,它一直被称为请求 1。一旦用户获得响应,请求就结束了。
- 现在,通过单击超链接、按钮或再次使用浏览器的地址栏,用户可以发出新请求。
查看下图以理解“单个请求周期”和 TempData。
代码示例:
public ActionResult M1()
{
TempData["a"] = "Value";
string s = TempData["a"].ToString(); // TempData will be available
return RedirectToAction("M2");
}
public ActionResult M2()
{
string s = TempData["a"].ToString(); //TempData will be available
return RedirectToAction ("M3");
}
public ActionResult M3()
{
string s = TempData["a"].ToString();// TempData will be available
return view ("Some View"); // TempData will be available inside view alsoJ
}
无论是视图还是动作方法,只要请求未完成,TempData 中的值就可用。
TempData 值何时会被删除?
- 在请求结束时,如果 TempData 中的值被标记为删除,它们将自动被删除。
- 一旦读取值,它们就会被标记为删除。
示例 1
public ActionResult M1()
{
TempData["a"] = "Value";
return view("MyView");
}
...
...
...
View Code
...
...
@{
string s = TempData["a"].ToString(); // TempData is marked for deletion
}
示例 2
public ActionResult M1()
{
TempData["a"] = "Value";
string s = TempData["a"].ToString(); // TempData is marked for deletion
return view("MyView"); // MyView can access TempData because it will be deleted at the end of the request
}
注意:在上面的两个示例中,值在被读取后立即被标记为删除。现在,对于下一个请求,这些值将不可用,因为在当前请求结束时,当前的 TempData 会被删除。
如果 TempData 值未被读取,会发生什么?
根据我们最后的解释,TempData 值将在当前请求结束时被删除,前提是它们在当前请求中被标记为删除,而它们只有在我们读取它们时才会被标记为删除。如果我们不读取它们,会发生什么?
简单来说,在这种情况下,TempData 也会在下一个请求中可用。现在,TempData 将在下一个请求结束时被删除,如果它在下一个请求中被标记为删除,否则这个故事将继续。
代码示例
public ActionResult M1()
{
TempData["a"] = "Value";
return view("MyView"); // TempData will be available inside view but let’s assume MyView won’t read the value
}
//Immediate next request after M1 made by end user
public ActionResult M2()
{
string s = TempData["a"].ToString(); // TempData will be available and now its marked for deletion
return view("MyView2"); // TempData will be available inside view also because request is not ended yet.
}
在上面的示例中,
- 用户向动作方法 M1 发出请求。它会创建 TempData,该 TempData 在该请求中始终可用,但在 M1 或 MyView 内部都没有读取值,因此在请求结束时值不会被删除。
- 接下来,假设用户通过超链接或其他方式向 M2 动作方法发出新请求。那么在 M1 动作方法期间创建的 TempData 在 M2 中也将可用。在 M2 内部,TempData 值将被读取,因此它被标记为删除。一旦用户获得最终响应,当前请求就结束了。在我们的例子中,用户只有在视图完成渲染后才能获得最终响应。因此,TempData 在视图中也可用。一旦视图渲染完成,TempData 就会被删除。
Keep 和 Peek 方法
Keep
Keep 方法使 TempData 中的值在下一个请求中保持持久。
示例 1
动作方法
public ActionResult M1()
{
TempData["a"] = "Value";
TempData["b"] = "Value1";
TempData["c"] = "Value2";
TempData.Keep();
return view("MyView");
}
视图
...
...
@{
string s= TempData["a"].ToString();
string s1= TempData["b"].ToString();
string s2= TempData["c"].ToString();
}
在上面的示例中,所有三个 TempData 值在下一个请求中也可用。
示例 2
动作方法
public ActionResult M1()
{
TempData["a"] = "Value";
TempData["b"] = "Value1";
TempData.Keep();
TempData["c"] = "Value2";
return view("MyView");
}
视图
...
...
@{
string s= TempData["a"].ToString();
string s1= TempData["b"].ToString();
string s2= TempData["c"].ToString();
}
在上面的示例中,只有键为“a”和“b”的 TempData 在下一个请求中可用。键为“c”的 TempData 在当前请求结束时被删除。
示例 3
操作方法
public ActionResult M1()
{
TempData["a"] = "Value";
TempData["b"] = "Value1";
TempData["c"] = "Value2";
TempData.Keep(“a”);
return view("MyView"); // TempData will be available inside view
}
视图
...
...
@{
string s= TempData["a"].ToString();
string s1= TempData["b"].ToString();
string s2= TempData["c"].ToString();
}
在上面的示例中,只有键为“a”的 TempData 在下一个请求中可用。
键为 b 和 c 的 TempData 将在当前请求结束时被删除。
窥视
Peek 允许我们在不将其标记为删除的情况下检索 TempData 值。
无 Peek 的示例
public ActionResult M1()
{
TempData["a"] = "Value";
return RedirectToAction("M2");
}
public ActionResult M2()
{
string s = TempData["a"].ToString(); //TempData will be marked for deletion
return view ("Some View");
}
.
.
.
//Next request
public ActionResult M3()
{
string s = TempData["a"].ToString(); // Will get an Exception
…
}
在上面的示例中,TempData 值(在动作方法 M1 中创建)在下一个请求中不可用,因为它已经在当前请求(在动作方法 M2 中)中使用了。
当用户向动作方法 M3 发出新请求时,将抛出空引用异常。
带 Peek 的示例
public ActionResult M1()
{
TempData["a"] = "Value";
return RedirectToAction("M2");
}
public ActionResult M2()
{
string s = TempData.Peek("a").ToString(); // TempData values will be read but will not be marked for deletion
return view ("Some View");
}
.
.
.
public ActionResult M3()
{
string s = TempData["a"].ToString(); // Will just work
…
}
在上面的示例中,TempData 值在下一个请求中也可用。
避免 Asp.Net MVC 中的 CSRF 攻击
在我们学习如何避免它之前,让我们先了解它。
CSRF 是跨站请求伪造的缩写。
在这里,黑客将开发一个两面 UI。两面 UI 意味着一个 UI 根据最终用户执行一些不同的操作,但实际上它会执行一些不同的操作。
为了更好地理解它,让我们做一个小演示。
步骤 1 – 打开 Day 7 项目
打开我们在 Day 7 中完成的项目。
步骤 2 – 执行项目
按 F5 并执行项目。使用 Admin 用户完成登录过程。
步骤 3 – 创建伪造项目
打开一个新的 Visual Studio。选择 文件 >> 新建 >> 项目。从左侧面板选择 Web 部分,从右侧选择“Asp.Net Web Application”。将其命名为“ForgeryProject”并单击确定。
选择 Empty 模板和 Web Forms 引用,然后单击确定。
(在此演示中,您正在创建此伪造项目,但在实际情况下,它将由想要入侵您系统的人创建。)
步骤 4 – 创建 DownloadEbook 选项
在新建项目中创建一个名为 DownloadEbook.html 的 HTML 页面,如下所示。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<form action="https://:8870/Employee/SaveEmployee" method="post" id="EmployeeForm">
<input type="hidden" name="FirstName" value="DummyFName" />
<input type="hidden" name="LastName" value="LName" />
<input type="hidden" name="Salary" value="35000" />
<input type="hidden" name="BtnSubmit" value="Save Employee" />
Congratulations!! You have won a free pdf of Learn MVC project in 7 days.
Email : - <input type="text" name="Whatver" value="" />
<input type="submit" name="MyButton" value="Send Link" />
</form>
</body>
</html>
步骤 4 – 执行项目
按 F5 并执行应用程序。
解释
您期望获得一个下载 pdf 的链接,但它实际上创建了一个新员工。
让我们来理解一下到底发生了什么。
- 最终用户只能在他是已认证的 Admin 用户时才能创建员工。
- 在我们的例子中,Admin 用户将使用他的 Admin 凭据登录,一段时间后,他将打开一个新的浏览器实例(或浏览器标签页)并开始浏览。(请记住,他当前的会话是活动的,他仍然是我们应用程序的已认证用户)
- 突然,上面由某个敌人创建的伪造页面出现在他面前,他/她单击“发送链接”按钮。
- “发送链接”按钮实际上将向我们的应用程序发送一个 POST 请求。您知道接下来会发生什么。
这就是所谓的 CSRF 攻击。
解决此问题的方案是使用令牌。
- 在实际数据录入屏幕中,服务器将在表单中注入一个秘密令牌作为隐藏字段。
- 这意味着当从实际数据录入屏幕发出 POST 请求时,秘密令牌将作为已发布数据的一部分发送。
- 在服务器端,将接收到的令牌与生成的令牌进行验证,如果它们不匹配,则请求将不会被处理。
现在让我们实现解决方案。
步骤 1 – 在原始数据录入屏幕中注入令牌
在 Day 7 项目的“~/Views/Employee”文件夹中打开 CreateEmployee.cshtml,并注入秘密令牌,如下所示。
<td colspan="2">
@Html.AntiForgeryToken()
<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>
步骤 2 – 在动作方法上启用伪造验证
将 ValidateAntiForgeryToken 属性附加到 EmployeeController 的 SaveEmployee 动作方法,如下所示。
[AdminFilter]
[HeaderFooterFilter]
[ValidateAntiForgeryToken]
public ActionResult SaveEmployee(Employee e, string BtnSubmit)
{
switch (BtnSubmit)
{
步骤 3 – 检查数据录入屏幕中生成的令牌
按 F5 并执行应用程序。完成登录过程。导航到 AddNew 屏幕并检查视图源。
步骤 4 – 检查伪造
保持项目登录打开并执行伪造项目,执行我们之前进行的相同测试。
现在它已经安全了。
MVC 捆绑和最小化
理解捆绑
我们无法想象一个没有 CSS 和 JavaScript 文件的项目。
捆绑是在运行时将多个 JavaScript 或 CSS 文件合并为一个文件的概念。现在,百万美元的问题是:为什么我们要合并?
为了更好地理解这一点,让我们做一个演示。
步骤 1 – 执行项目
再次执行我们的项目。在 Chrome 中执行。
步骤 2 – 打开 Chrome 开发者工具
在 Chrome 中按“cntrl + shift + I”打开开发者工具。然后导航到“Network tab”。
步骤 3 – 打开登录
现在向登录页面发出请求,并再次检查网络选项卡。
如果您还记得,我们在登录视图中实现了无提示客户端验证,这需要三个 JavaScript 文件。因此,当请求登录视图时,它会进行四次请求调用:
- 一次用于登录视图。
- 三次请求用于 JavaScript 文件“jQuery.js”、“jQuery.validate.js”和“jQuery.validate.unobtrusive.js”。
试想一下,当有很多 JavaScript 文件时的情况。这会导致多次请求,从而降低性能。
解决方案是将所有 JS 文件合并到一个捆绑包中,并以一次请求作为一个单元来请求它。这个过程称为捆绑。
我们很快将学习如何使用 Asp.Net MVC 进行捆绑。
理解最小化
最小化通过删除空白字符、注释等来减小脚本和 CSS 文件的大小。例如,下面是一个带有注释的简单 JavaScript 代码。
// This is test
var x = 0;
x = x + 1;
x = x * 2;
实现最小化后,JavaScript 代码看起来如下所示。您可以看到如何删除空格和注释以减小文件大小,从而通过减小文件大小来提高性能。
var x=0;x=x+1;x=x*2;
在 Asp.Net MVC 中实现捆绑和最小化
步骤 1 – 创建脚本捆绑包
打开 App_Start 文件夹中的 BundleConfig.cs 文件。您会找到一个 RegisterBundle 方法。删除其中的所有现有代码,并按如下方式重新定义它。
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jqueryValidation").Include(
"~/Scripts/jquery-1.8.0.js").Include(
"~/Scripts/jquery.validate.js").Include(
"~/Scripts/jquery.validate.unobtrusive.js"));
}
步骤 2 – 在登录视图中包含捆绑包
从“~/Views/Authentication”文件夹中打开 Login.cshtml 视图。
删除三个脚本文件的引用,并将其包含为一个捆绑包。
<title>Login</title>
@Scripts.Render("~/bundles/jqueryValidation")
</head>
<body>
步骤 3 – 执行和测试
执行应用程序,然后再次检查网络选项卡。
不幸的是,您将找不到任何区别。
步骤 4 – 启用捆绑和最小化
打开 Global.asax 文件,并在 Application_Start 事件的顶部添加以下行。
BundleTable.EnableOptimizations = true;
第五步——执行和测试
再次执行相同的测试。
正如您所见,只需要请求一个文件而不是三个文件。所有三个 JavaScript 文件都被捆绑成一个。检查大小。它只有 294B。之前是(289+294+310)。这证明最小化也完成了它的工作。
(别忘了检查验证。☻)
同样,我们为 CSS 文件创建名为 StyleBundle 的捆绑包。
创建自定义辅助类
这更多的是 C# 而不是 Asp.Net MVC。
在本文中,我们将为提交按钮创建一个自定义辅助函数。
步骤 1 – 创建一个 CustomHelperClass
在项目中创建一个名为 Extensions 的新文件夹,并在其中创建一个名为 CustomHelperMethods 的新类。
步骤 2 – 使类成为静态类
现在将上述类设为静态类,因为这是扩展方法的第一项要求。
namespace WebApplication1.Extenions
{
public static class CustomHelperMethods
{
}
}
步骤 3 – 创建一个扩展方法
在类中按如下方式添加 using 语句。
usingSystem.Web.Mvc;
在上述类中创建一个名为 Submit 的新的静态扩展方法,如下所示。
public static MvcHtmlString Submit(this HtmlHelper helper, string displayText)
{
return MvcHtmlString.Create
(
string.Format("<input type='submit' name=Btn{0} id=Btn{0} value={0} />",displayText)
);
}
步骤 4 – 更改登录视图
从“~/Views/Authenticate”文件夹中打开登录视图,并按如下方式在其中添加 using 语句。
@using WebApplication1.Extenions
现在删除 input type submit,并将其替换为以下代码。
...
...
@using (Html.BeginForm("DoLogin", "Authentication", FormMethod.Post))
{
@Html.LabelFor(c=>c.UserName)
@Html.TextBoxFor(x=>x.UserName)
@Html.ValidationMessageFor(x=>x.UserName)
<br />
@Html.LabelFor(c => c.Password)
@Html.PasswordFor(x => x.Password)
<br />
@Html.Submit("Login")
}
...
...
步骤 5 - 执行和测试
按 F5 并执行应用程序。检查输出。
MVC 单元测试
对于这一点,我们不会做一个完整的演示。我想让您将其作为一项作业。
有关参考,请参阅以下文章。
https://codeproject.org.cn/Articles/763928/WebControls
开始使用 MVC 5
如果您想从 MVC 5 开始,请从下面的视频“2 天学会 MVC 5”开始。
结论
非常感谢大家提出的精彩评论和反馈。
您的评论、邮件总是激励我们做得更多。请在下方发表您的想法和评论或发送邮件至 SukeshMarla@Gmail.com
在 Facebook、LinkedIn 或 twitter 上与我们联系,以获取新版本更新。
如需在孟买进行线下技术培训,请访问 StepByStepSchools.Net
如需在线培训,请访问 JustCompile.com 或 www.Sukesh-Marla.com
玩得开心,继续学习。我们将与新的系列再次见面☻