7 天玩转 ASP.NET MVC — 第 4 天

目录

0. 前言

欢迎来到第四天的 MVC 系列学习中。如果你直接开始学习今天的课程,我强烈建议你先完成之前的学习内容再来到这里。

1. Lab 15 — 认证错误的保留值

在 Lab 13 中,我们介绍了服务器端的认证,并且在 Lab 14 中,我们通过添加自定义认证的方式将其提示到一个新的层级。

我强烈建议你再回顾一下 Lab 14。再次执行应用,并且能够很好地理解代码以及输出。

在 Lab 15 中,我们将学习如何在认证失败时填充值。

第一步:创建 CreateEmployeeViewModel

在 ViewModel 文件夹下创建一个新的类。

public class CreateEmployeeViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Salary { get; set; }
}

第二步:改变 SaveEmployee 行为方法

我们将重新使用 Model Binder 创建的 Employee 对象来重新生成。改变 SaveEmployee 行为方法如下。

public ActionResult SaveEmployee(Employee e, string BtnSubmit)
{
    switch (BtnSubmit)
    {
        case "Save Employee":
            if (ModelState.IsValid)
            {
                EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
                empBal.SaveEmployee(e);
                return RedirectToAction("Index");
            }
            else
            {
                CreateEmployeeViewModel vm = new CreateEmployeeViewModel();
                vm.FirstName = e.FirstName;
                vm.LastName = e.LastName;
                if (e.Salary.HasValue)
                {
                    vm.Salary = e.Salary.ToString();                        
                }
                else
                {
                    vm.Salary = ModelState["Salary"].Value.AttemptedValue;                       
                }
                return View("CreateEmployee", vm); // Day 4 Change - Passing e here
            }
        case "Cancel":
            return RedirectToAction("Index");
    }
    return new EmptyResult();
}

第三步:在视图中重新填值

  • 将 View 成为一个强类型视图

在 CreateEmployee 视图的顶部,放置如下代码。

@using WebApplication1.ViewModels
@model CreateEmployeeViewModel
  • 在相应控件中呈现从 Model 中获取的值

    ...
    ...
    <input type="text" id="TxtFName" name="FirstName" value="@Model.FirstName" />
    ...
    ...
    <input type="text" id="TxtLName" name="LastName" value="@Model.LastName" />
    ...
    ...
    <input type="text" id="TxtSalary" name="Salary" value="@Model.Salary" />
    ...
    ...

第四步:执行并测试

按下 F5 执行应用。通过点击“Add New”链接导航到 AddNew 屏幕上。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

上述的错误将会在实验结束后探讨。现在让我们来实现解决方案。

第五步:改变 AddNew 行为方法

public ActionResult AddNew()
{
    return View("CreateEmployee”, new CreateEmployeeViewModel());
}

第六步:执行并测试

按下 F5,并执行应用。

  • Test

步骤如下。

  1. 通过点击「Add New」链接导航到 AddNew 屏幕。

  2. 保持 First Name 为空。

  3. 将 Salary 设置为 56。

  4. 点击「Save Employee」按钮。

这样会使得两个认证是失败的。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

正如你所看见的那样,值56 仍然保留在 Salary 文本框内。

  • Test 2
7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

正如你所看见的,FirstName 和 LastName 文本框内的值仍然有所保留。
但是奇怪的是,Salary 并没有保留值。我们将会在实验的最后探讨原因并给出解决方案。

Lab 15 的 Q&A

我们真的将值保留了吗?

答案是否定的。实际上,我们重填的值是从 Posted 数据中获取的。

为什么在初始的请求中,在「AddNew」行为方法中需要传输「new CreateEmployeeViewModel()」?

在视图中,我们尝试将模型中的值重新填充到文本框内。例如:

<input id="TxtSalary" name="Salary" type="text" value="@Model.Salary" />

正如你所看见的,在代码区域,我们访问当前模型的 FirstName 属性。如果 Model 为 Null,那么将会抛出「Object reference not set to an instance of the class」的异常。

当点击「Add New」超链接时,请求将会被「Add New」行为方法处理。在该方法中,我们可以在返回视图时不传输任何数据。这意味着视图中 Model 的属性为 Null,并且会抛出「Object reference not set to an instance of the class」的异常。为了解决这个问题,在初始请求中,需要传输「new CreateEmployeeViewModel()」。

我们可以通过自动的方式来达到上述同样的功能效果吗?

答案是肯定的。我们可以运用 HTML Helper 类来解决。我们将会在接下来的实验中探讨这个问题。

2. Lab 16 — 添加客户端认证

首先我们列举所需要的所有认证。

  1. FirstName 不应为空。

  2. LastName 的长度不能超出5。

  3. Salary 不应为空。

  4. Salary 应该为一个正确的数字。

  5. FirstName 应该不能包含「@」符号。

让我们来实现它吧。

第一步:创建一个 JavaScript 认证文件

创建一个 JavaScript 文件,命名为「Validations.js」,并且把它放到 Scripts 文件夹内。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

第二步:创建认证函数

在「Validations.js」文件内,创建一个认证函数。

function IsFirstNameEmpty() {
    if (document.getElementById('TxtFName').value == "") {
        return 'First Name should not be empty';
    }
    else { return ""; }
}

function IsFirstNameInValid() {    
    if (document.getElementById('TxtFName').value.indexOf("@") != -1) {
        return 'First Name should not contain @';
    }
    else { return ""; }
}
function IsLastNameInValid() {
    if (document.getElementById('TxtLName').value.length>=5) {
        return 'Last Name should not contain more than 5 character';
    }
    else { return ""; }
}
function IsSalaryEmpty() {
    if (document.getElementById('TxtSalary').value=="") {
        return 'Salary should not be empty';
    }
    else { return ""; }
}
function IsSalaryInValid() {
    if (isNaN(document.getElementById('TxtSalary').value)) {
        return 'Enter valid salary';
    }
    else { return ""; }
}
function IsValid() {

    var FirstNameEmptyMessage = IsFirstNameEmpty();
    var FirstNameInValidMessage = IsFirstNameInValid();
    var LastNameInValidMessage = IsLastNameInValid();
    var SalaryEmptyMessage = IsSalaryEmpty();
    var SalaryInvalidMessage = IsSalaryInValid();

    var FinalErrorMessage = "Errors:";
    if (FirstNameEmptyMessage != "")
        FinalErrorMessage += "\n" + FirstNameEmptyMessage;
    if (FirstNameInValidMessage != "")
        FinalErrorMessage += "\n" + FirstNameInValidMessage;
    if (LastNameInValidMessage != "")
        FinalErrorMessage += "\n" + LastNameInValidMessage;
    if (SalaryEmptyMessage != "")
        FinalErrorMessage += "\n" + SalaryEmptyMessage;
    if (SalaryInvalidMessage != "")
        FinalErrorMessage += "\n" + SalaryInvalidMessage;

    if (FinalErrorMessage != "Errors:") {
        alert(FinalErrorMessage);
        return false;
    }
    else {
        return true;
    }
}

第三步:在 View 中包含认证文件

在「CreateEmployee」视图的顶部,将「Validations.js」文件引入。

<script src="~/Scripts/Validations.js"></script>

第四步:附加认证

在 SaveEmployee 按钮点击时触发 IsValid 函数。

<input type="submit" name="BtnSubmit" value="Save Employee" onclick="return IsValid();" />

第五步:执行并测试

按下 F5,执行应用。

通过点击「Add New」链接导航到 AddNew 屏幕。

  • Test 1
7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天
  • Test 2
7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

Lab 16 的 Q&A

为什么点击 SaveEmployee 按钮时,关键字需要被返回?

正如我们在 Lab 9 中所探讨的,提交按钮被点击时,它将会向服务器发送请求。当认证失败时,向服务器发送请求是没有意义的。通过在提交按钮中写下「return false」,我们可以阻止默认的服务器请求。

在我们的这个例子中,当认证失败时,IsValid 函数将会返回 false,因此我们达到了预期的功能。

除了弹出的警告以外,我们可以在页面本身上显示错误信息吗?

答案是肯定的。只需要为每个错误创建一个 Span 标签。运用 CSS 使它在开始时不可见,当提交按钮点击后,如果认证失败了,运用 JavaScript 使其可见。

是否存在客户端自动认证的方法?

答案是肯定的。当我们运用 HTML Helper 就可以基于服务端认证实现自动客户端认证。我们将会在后面的实验中探讨这个问题。

服务器端的认证还被需要吗?

答案是肯定的。在一些场景下,JavaScript 无法使用,服务器端认证可以替代它使用。

3. Lab 17 — 添加授权认证

在这个实验中,我们将会使得 GetView 方法更加安全。我们将会确保只有合法用户才能访问行为方法。

在第一天的系列学习中,我们了解了 ASP.NET 和 MVC 的真实含义。我们理解到 ASP.NET MVC 是 ASP.NET 的一部分。许多 ASP.NET 的功能都被 ASP.NET MVC 所继承。其中一个功能便是表单的认证。

在我们开始实验之前,我们先了解下在 ASP.NET 中如何进行表单认证工作。

  1. 终端用户通过浏览器发送一个表单认证的请求。

  2. 浏览器将发送所有请求相关的Cookies,存储在客户机器上。

  3. 当请求被服务器端接收到,服务器会检查请求并检查特殊的 Cookie,即「Authentication Cookie」。

  4. 如果发现了合法的认证 Cookie,服务器就会证实用户的身份,或者简单来说,认为用户是一个合法的用户,并允许他进行下一步。

  5. 如果没有发现合法的认证 Cookie,服务器就会认为用户是匿名(未认证)的用户。在这种情况下,如果被请求的资源被标记为 Protected/Secured,那么用户将会被重新导向到登录页面。

第一步:创建一个 AuthenticationController 和一个登录行为方法

右击 Controller 文件夹,选择「Add New Controller」,然后创建一个控制器,命名为「Authentication」。在这种情况下,全名应该为「AuthenticationController」。

在控制器内创建一个行为方法,命名为 Login。

public class AuthenticationController : Controller
{
    // GET: Authentication
    public ActionResult Login()
    {
        return View();
    }
}

第二步:创建 Model

在 Model 文件夹下创建一个 Model 类,命名为 UserDetails。

namespace WebApplication1.Models
{
    public class UserDetails
    {
        public string UserName { get; set; }
        public string Password { get; set; }
    }
}

第三步:创建 Login 视图

在「~/Views/Authentication」文件夹下创建一个新的视图,命名为 Login。使它成为一个 UserDetails 的强类型视图。

在视图内放置如下 HTML 代码。

@model WebApplication1.Models.UserDetails

@{

    Layout = null;

}

<!DOCTYPE html>

<html>

<head>

    <meta name="viewport" content="width=device-width" />

    <title>Login</title>

</head>

<body>

    <div>

        @using (Html.BeginForm("DoLogin", "Authentication", FormMethod.Post))

        {

            @Html.LabelFor(c=>c.UserName)

            @Html.TextBoxFor(x=>x.UserName)

       

            <br />

            @Html.LabelFor(c => c.Password)

            @Html.PasswordFor(x => x.Password)

            <br />

            <input type="submit" name="BtnSubmit" value="Login" />

        }

    </div>

</body>

</html>

正如你所看见的,这次我们生成的视图运用了 HtmlHelper 类,而不是纯 HTML。

  • 在视图中我们已经创建了一个 HtmlHelper 类的对象,即「Html」。

  • HtmlHelper 类的功能简单返回 HTML 字符串。

Example 1

@Html.TextBoxFor(x=>x.UserName)

上述的代码将产生如下的 HTML。

<input id="UserName" name="UserName" type="text" value="" />

Example 2

@using (Html.BeginForm("DoLogin", "Authentication", FormMethod.Post))
{
}

上述的代码将产生如下的 HTML。

<form action="/Authentication/DoLogin" method="post">
</form>

第四步:执行并测试

按下 F5,执行应用。在地址栏输入 Login 方法的 URL。在这个例子中,将会是「http://localhost:8870/Authentication/Login」。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

第五步:授权表单认证

打开 Web.config 文件。导航到 System.Web 区域。找到子区域 Authentication。如果这里不存在就创建一个。设置 Authentication 的模式是 Forms,Login URL 指向第一步所创建的「Login」的行为方法。

<authentication mode="Forms">
<forms loginurl="~/Authentication/Login"></forms>
</authentication>

第六步:让行为方法更加安全

打开 EmployeeController,然后向 Index 方法附上 Authorize 属性。

[Authorize]
public ActionResult Index()
{
    EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
......

第七步:执行并测试

按下 F5 并执行应用。在地址栏中输入 EmployeeController 的 Index 行为方法的 URL。在这个情景中,URL 为“http://localhost:8870/Employee/Index”。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

正如你所看见的,Index 行为的请求将会重指向 Login 行为。

第八步:创建一个业务层功能

打开 EmployeeBusinessLayer 类,然后创建一个方法叫做 IsValidUser。

public bool IsValidUser(UserDetails u)
{
    if (u.UserName == "Admin" && u.Password == "Admin")
    {
        return true;
    }
    else
    {
        return false;
    }
}

注:在业务层,我们对比 UserName 和 Password 的硬编码值。在真实场景中,我们能够调用数据库层,并对比真实的值。

第九步:创建 DoLogin 行为方法

打开 AuthenticationController 类,然后创建一个新的行为方法,叫做 DoLogin。

这个 DoLogin 方法会在 Login 按钮点击时被触发。

现在我们来列举一下 DoLogin 中需要做的几点。

  1. 通过触发业务层功能来检查用户的合法性。

  2. 如果用户是一个合法用户,就创建一个认证的 Cookie。它能够使得未来的请求是认证过的请求。

  3. 如果用户是不合法的,就向当前的 ModelState 增加一个错误。这个错误将会呈现在视图中。

    [HttpPost]
    public ActionResult DoLogin(UserDetails u)
    {
    EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
    if (bal.IsValidUser(u))
    {
    FormsAuthentication.SetAuthCookie(u.UserName, false);
    return RedirectToAction("Index", "Employee");
    }
    else
    {
    ModelState.AddModelError("CredentialError", "Invalid Username or Password");
    return View("Login");
    }
    }

让我们理解下上述代码区域。

  • 如果你记得「第三天 — Lab 13」中我们所谈论的 ModelState,那么就能理解它包含了模型的状态。它包含了当前模型的错误信息。在上述的代码片段中,当用户是一个非法用户时,就增加一个新的错误。(错误由键值「CredentialError」和信息「Invalid UserName or Password」组成)

  • 表单认证。SetAuthCookie 将会在客户机器上创建一个新的 Cookie。

第十步:在视图中呈现信息

打开 Login 视图,在 @Html.BeginForm 上增加一小段代码。

@Html.ValidationMessage("CredentialError", new {style="color:red;" })
@using (Html.BeginForm("DoLogin", "Authentication", FormMethod.Post))

第十一步:执行并测试

按下 F5 并执行应用。直切发起一个指向 Login Action 的请求。我相信你现在已经知道如何去做。

注:如果你想发起一个指向 EmployeeController 中 Index Action 的请求,它会重新导向到 Login Action。

  • Test 1
7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天
  • Test 2
7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

Lab 17 的 Q&A

为什么 DoLogin 会附上 HttpPost 属性?

这个属性可以使 DoLogin 行为方法只为 Post 请求开启。如果想试图得到一个 DoLogin 的请求,将不会起作用。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

我们还有其它类似的属性吗?

答案是肯定的。我们有 HttpGet,HttpPut 和 HttpDelete。作为一个最佳实践,每一个行为方法都应该被附上这样的属性。

注意:为了保持代码和学习是轻松并简单的,我们在这个系列学习中并没有全部都遵循最佳实践,但是我们建议你在项目中遵循。

在我们持续的学习中,我们将会继续谈论最佳实践。

FormsAuthentication.SetAuthCookie 是必须要写的吗?

答案是肯定的。

让我们来理解一个小的程序。

  • 用户通过浏览器向服务器发送请求。

  • 当请求通过浏览器发出,所有相关联的 Cookies 将会伴随着请求。

  • 服务器接收到请求并且准备发出响应。

  • 现在我们已经知道请求和响应是通过 HTTP 协议,并且 HTTP 是无关国籍的。对于服务器而言,每一个请求都是一个新的请求,因此当相同的用户发出两次请求时,服务器并不能够识别它。为了解决这个问题,服务器在准备响应的时候增加了 Cookie,然后返回响应。

  • 当客户端浏览器接收到伴随着 Cookie 的响应后,它将会在客户端创建 Cookies。

  • 现在客户又一次发出请求,服务器将会识别他,因为请求中包含了 Cookies。

FormsAuthentication.SetAuthCookie 将会向响应增加一个特殊的 Cookie,命名为「Authentication」。

如果没有 Cookies,是不是意味着 FormsAuthentication 就不会起作用?

答案是否定的。我们有替代物。我们可以运用 URI,而不是 Cookies。

打开 Web.config,然后更改「Authentication/Forms」区域如下。

<forms cookieless="UseUri" loginurl="~/Authentication/Login"></forms>
7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

正如你所看见的,现在认证 Cookie 通过 URL 它自己传输。

默认情况下,Cookieless 属性会被设置为「AutoDetect」。这意味着认证工作通过 Cookie 完成,在这种情景下,Cookies 将不会支持通过 URL 传输。

FormsAuthentication.SetAuthCookie 的第二个参数起到什么作用?

这决定了我们是否愿意创建一个永久的 Cookie。非永久的 Cookie 将会在浏览器关闭时自动删除。永久性的 Cookies 将不会被自动删除。我们可以通过代码或者浏览器设置手动删除。

通过代码做到登出?

我们将会在以后的 Lab 中学习。

当认证失败时,UserName 文本框将会如何被填充?

这是 HTML Helper 类的神奇之处。它们将会在控件中填充来自 Posted 数据的值。这是运用 HTML Helper 类的其中一个优势。

@Html.ValidationMessage 是做什么的?

我们在 Lab 13 中已经探讨过了。它根据键值呈现 ModelState 的错误。

Authorize 属性是做什么的?

在 ASP.NET MVC 中,存在一个概念叫做 Filters。运用它可以过滤请求和响应。这里存在四种过滤器。我们将会在第七天的学习中探讨它们。Authorize 属性通过 Authorization 过滤器得出。这样可以确保只有认证过的请求允许指向动作方法。

我们可以在一个行为方法中同时附上 HttpPost 和 Authorize 属性吗?

答案是肯定的。

在这个例子中,为什么没有 ViewModel?

在 Lab 6 中我们所探讨的,View 应该和 Model 直接相关联。我们必须有 View 和 Model 中的 ViewModel。视图是展示视图或者数据入口视图都不重要,它们都应该和 ViewModel 相关联。在真实项目中,我强烈希望你全部都运用上 ViewModel。

对于每一个行为方法都必须附上 Authorize 属性吗?

答案是否定的。我们可以附上它 Controller 级别或者 Global 级别。当附上 Controller 级别,它将可以适用于一个控制器里的所有行为方法。当附上 Global 级别,它将会适用于所有控制器里的所有行为方法。

  • Controller 级别:

    [Authorize]
    public class EmployeeController : Controller
    {
    ....

  • Global 级别:

第一步:从 App_start 文件夹下打开 FilterConfig.cs 文件

第二步:在 RegisterGlobalFilters 中增加一行

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());//Old line
    filters.Add(new AuthorizeAttribute());//New Line
}

第三步:向 AuthenticationController 附上 AllowAnonymous 属性

[AllowAnonymous]
public class AuthenticationController : Controller
{

第四步:执行并测试应用

「filters.Add(new HandleErrorAttribute())」是用来做什么的?

我们将会在日后的试验中探讨这个问题的细节。

为什么 AuthenticationController 中需要加上 AllowAnonymous 属性?

我们已经附上了 Global 级别的 Authorize 过滤器。这意味着所有的方法都将是受保护的,包括 Login 和 DoLogin 行为方法。AllowAnonymous 为未认证的请求开启了行为方法。

FilterConfig 类中的 RegisterGlobalFilters 方法如何被触发的?

它通过 Global.asax 文件内的 Application_Start 事件所触发。

4. Lab 18 — 在视图中展示 UserName

在本实验中,我们将在视图中呈现当前登录的用户名。

第一步:在 ViewModel 中增加 UserName

打开 EmployeeListViewModel,然后增加一个新的属性,即 UserName。

public class EmployeeListViewModel
{
    public List<EmployeeViewModel><employeeviewmodel> Employees { get; set; }
    public string UserName { get; set; }
}

第二步:向 ViewModel 的UserName 设置值

打开 EmployeeController,然后更改 Index 方法如下。

public ActionResult Index()
{
    EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
    employeeListViewModel.UserName = User.Identity.Name; //New Line
......

第三步:在视图中呈现 UserName

打开 Index.cshtml 视图,呈现 UserName。

<body>
  <div style="text-align:right"> Hello, @Model.UserName </div>
  <hr />
  <a  href="/Employee/AddNew">Add New</a>
    <div>
       <table border="1"><span style="font-size: 9pt;"> 
</span>

第四步:执行并测试

按下 F5 并执行应用。完成登录操作后,你将会看到如下输出。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

5. Lab 19 — 实现登出

第一步:创建登出链接

打开 Index.cshtml 文件然后创建登出链接。

<body>
    <div style="text-align:right">Hello, @Model.UserName
    <a href="/Authentication/Logout">Logout</a></div>
    <hr />
    <a  href="/Employee/AddNew">Add New</a>
    <div>
        <table border="1">

第二步:创建登出行为方法

打开 AuthenticationController,然后增加一个行为方法,命名为 Logout。

public ActionResult Logout()
{
    FormsAuthentication.SignOut();
    return RedirectToAction("Login");
}

第三步:执行并测试

按下 F5 并执行应用。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

6. Lab 20 — 在 Login 页实现认证

第一步:增加数据注释

打开 UserDetails.cs,然后增加 Data Annotation。

public class UserDetails
{

[StringLength(7,MinimumLength=2, ErrorMessage = "UserName length should be between 2 and 7")]
    public string UserName { get; set; }
    public string Password { get; set; }
}

第二步:在视图中呈现错误信息

改变 Login.cshtml,呈现错误信息。

@using (Html.BeginForm("DoLogin", "Authentication", FormMethod.Post))
{
    @Html.LabelFor(c=>c.UserName)
    @Html.TextBoxFor(x=>x.UserName)
    @Html.ValidationMessageFor(x=>x.UserName)
......

注:这次我们运用 Html.ValidationMessageFor,而不是Html.ValidationMessage。两个做的是同一件事。Html.ValidationMessageFor 只能用于强类型视图。

第三步:改变 DoLogin

改变 DoLogin 行为方法如下:

[HttpPost]
public ActionResult DoLogin(UserDetails u)
{
    if (ModelState.IsValid)
    {
        EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
        if (bal.IsValidUser(u))
        {
            FormsAuthentication.SetAuthCookie(u.UserName, false);
            return RedirectToAction("Index", "Employee");
        }
        else
        {
            ModelState.AddModelError("CredentialError", "Invalid Username or Password");
            return View("Login");
        }
    }
    else
    {
        return View("Login");
    }
}

第四步:执行并测试

按下 F5 并执行应用

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

7. Lab 21 — 在 Login 页实现客户端的认证

这次我们将以不同的方式实现客户端的认证。

第一步:下载 JQuery Unobtrusive Validation 文件

右击项目,选择「Manage Nuget Packages」。

点击 Online,然后搜索「JQuery Unobtrusive」。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

安装「Microsoft JQuery Unobtrusive Validation」。

第二步:在视图中引入 JQuery Validation 文件

在 Scripts 文件夹下增加三个 JavaScripts 文件。

  • JQuery-Someversion.js

  • JQuery.validate.js

  • JQuery.validate.unobtrusive

打开 Login.cshtml 文件,然后在头部引入这三个 JavaScript 文件。

<script src="~/Scripts/jquery-1.8.0.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>

第三步:执行并测试

按下 F5,执行应用。

7 天玩转 ASP.NET MVC — 第 4 天
7 天玩转 ASP.NET MVC — 第 4 天

8. Lab 21 的 Q&A

客户端的认证是如何实现的?

正如你所看见的,不费多少力气就实现了客户端的认证。在 Login 视图中,HTML 元素通过 HTML Helper 类产生。Helper 函数能够根据数据注释属性来生成带有属性附加的 HTML 标记。

例如:

@Html.TextBoxFor(x=>x.UserName)
@Html.ValidationMessageFor(x=>x.UserName)

上述的代码产生如下的 HTML 代码。

<input data-val="true" data-val-length="UserName length should be between 2 and 7" data-val-length-max="7" data-val-length-min="2" id="UserName" name="UserName" type="text" value="" />
<span class="field-validation-error" data-valmsg-for="UserName" data-valmsg-replace="true"> 
</span>

这些自定义的 HTML 属性将会被「JQuery Unobtrusive Validation」文件使用,因此在客户端自动实现认证。

自动的客户端认证是 HTML Helper 类的第二个优势。

Unobtrusive JavaScript 的意思是什么?

下面是 Wikipedia 所述。译文为:

Unobtrusive JavaScript 是一个在 Web 页面中常用的 JavaScript 方法。尽管它没有被正式定义,但是它的几个基本的准则可以被大概理解:

  • 从 Web 页面的结构或者内容中分离的功能("Behaviour Layer"),并且展示。

  • 传统的 JavaScript 编程中,最佳避免问题的实践。(例如浏览器的不一致性和缺乏伸缩性)

  • 逐步增强支持用户可能不先进的 JavaScript 功能。

让我以外行来定义一下。

「以一种方式来写你的 JavaScript,这种 JavaScript 不能与 HTML 强联系。JavaScript 可能访问 DOM 元素,JavaScript 可能操作 DOM 元素,但是并不和它们直接联系。」

在上述例子中,JQuery Unobtrusive JavaScript 简单地运用一些输入元素属性和实现客户端认证。

我们可以运用这些 JavaScript 来认证,而不是采用 HTML Helper 类吗?

答案是肯定的,对于这种方式,我们需要手动地向元素附件属性。

哪个更推崇,是 HTML Helper 函数还是纯 HTML?

我个人更倾向于纯 HTML,因为 HTML Helper 函数又一次用到了「Full Control over HTML」,而这个弊端我们曾讨论过。

其次我们讨论下使用 JavaScript 框架或库,而不是使用 JQuery 的项目情况。一些框架例如 Angular。在这种情况下,大多数我们都会考虑 Angular 认证,并且自定义的 HTML 认证属性将会抛弃。

9. 总结

现在我们已经完成了第四天的学习。在第五天中,我们将会有更进一步的学习,会更有乐趣。

原文地址:Learn MVC Project in 7 days

本文系 OneAPM 工程师编译整理。OneAPM 是应用性能管理领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。想阅读更多技术文章,请访问 OneAPM 官方博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容