65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (43投票s)

2015年7月27日

CPOL

11分钟阅读

viewsIcon

83181

downloadIcon

6013

本文是“7天学会MVC项目”系列文章的续篇。

引言

本文是“7天学会MVC项目”系列文章的续篇。正如我承诺的,这里是该系列的第二篇奖励日文章。这是本系列的最后一篇文章。我相信您已经享受了完整的系列。感谢您的所有支持,祝您好运。

今天我们将讨论一些在项目中尚未涵盖的 Asp.Net MVC 的主题。

完整系列

我们很高兴地宣布,本文现已推出实体书版本,您可以在 www.amazon.comwww.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

FacebookLinkedIntwitter 上与我们联系,以获取新版本更新。

如需在孟买进行线下技术培训,请访问 StepByStepSchools.Net

如需在线培训,请访问 JustCompile.comwww.Sukesh-Marla.com

玩得开心,继续学习。我们将与新的系列再次见面☻

© . All rights reserved.