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

目錄

0. 前言

歡迎來到第四天的 MVC 系列學習中无蜂。如果你直接開始學習今天的課程妖啥,我強烈建議你先完成之前的學習內容再來到這里。

1. Lab 15 — 認證錯誤的保留值

在 Lab 13 中受扳,我們介紹了服務器端的認證乘盼,并且在 Lab 14 中螟凭,我們通過添加自定義認證的方式將其提示到一個新的層級。

我強烈建議你再回顧一下 Lab 14菱涤。再次執(zhí)行應用苞也,并且能夠很好地理解代碼以及輸出。

在 Lab 15 中粘秆,我們將學習如何在認證失敗時填充值如迟。

第一步:創(chuàng)建 CreateEmployeeViewModel

在 ViewModel 文件夾下創(chuàng)建一個新的類。

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

第二步:改變 SaveEmployee 行為方法

我們將重新使用 Model Binder 創(chuàng)建的 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
  • 在相應控件中呈現(xiàn)從 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" />
    ...
    ...

第四步:執(zhí)行并測試

按下 F5 執(zhí)行應用劳吠。通過點擊“Add New”鏈接導航到 AddNew 屏幕上。

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

上述的錯誤將會在實驗結束后探討」茫現(xiàn)在讓我們來實現(xiàn)解決方案痒玩。

第五步:改變 AddNew 行為方法

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

第六步:執(zhí)行并測試

按下 F5,并執(zhí)行應用议慰。

  • 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 天

正如你所看見的拍霜,F(xiàn)irstName 和 LastName 文本框內的值仍然有所保留嘱丢。
但是奇怪的是,Salary 并沒有保留值祠饺。我們將會在實驗的最后探討原因并給出解決方案越驻。

Lab 15 的 Q&A

我們真的將值保留了嗎?

答案是否定的。實際上缀旁,我們重填的值是從 Posted 數(shù)據(jù)中獲取的记劈。

為什么在初始的請求中,在「AddNew」行為方法中需要傳輸「new CreateEmployeeViewModel()」诵棵?

在視圖中抠蚣,我們嘗試將模型中的值重新填充到文本框內。例如:

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

正如你所看見的履澳,在代碼區(qū)域嘶窄,我們訪問當前模型的 FirstName 屬性。如果 Model 為 Null距贷,那么將會拋出「Object reference not set to an instance of the class」的異常柄冲。

當點擊「Add New」超鏈接時,請求將會被「Add New」行為方法處理忠蝗。在該方法中现横,我們可以在返回視圖時不傳輸任何數(shù)據(jù)。這意味著視圖中 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 應該為一個正確的數(shù)字亿絮。

  5. FirstName 應該不能包含「@」符號。

讓我們來實現(xiàn)它吧麸拄。

第一步:創(chuàng)建一個 JavaScript 認證文件

創(chuàng)建一個 JavaScript 文件派昧,命名為「Validations.js」,并且把它放到 Scripts 文件夾內感帅。

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

第二步:創(chuàng)建認證函數(shù)

在「Validations.js」文件內斗锭,創(chuàng)建一個認證函數(shù)地淀。

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 按鈕點擊時觸發(fā) IsValid 函數(shù)。

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

第五步:執(zhí)行并測試

按下 F5实苞,執(zhí)行應用豺撑。

通過點擊「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 中所探討的,提交按鈕被點擊時猾浦,它將會向服務器發(fā)送請求陆错。當認證失敗時,向服務器發(fā)送請求是沒有意義的金赦。通過在提交按鈕中寫下「return false」音瓷,我們可以阻止默認的服務器請求。

在我們的這個例子中夹抗,當認證失敗時绳慎,IsValid 函數(shù)將會返回 false,因此我們達到了預期的功能漠烧。

除了彈出的警告以外杏愤,我們可以在頁面本身上顯示錯誤信息嗎?

答案是肯定的已脓。只需要為每個錯誤創(chuàng)建一個 Span 標簽珊楼。運用 CSS 使它在開始時不可見,當提交按鈕點擊后摆舟,如果認證失敗了亥曹,運用 JavaScript 使其可見。

是否存在客戶端自動認證的方法恨诱?

答案是肯定的媳瞪。當我們運用 HTML Helper 就可以基于服務端認證實現(xiàn)自動客戶端認證。我們將會在后面的實驗中探討這個問題照宝。

服務器端的認證還被需要嗎蛇受?

答案是肯定的。在一些場景下厕鹃,JavaScript 無法使用兢仰,服務器端認證可以替代它使用。

3. Lab 17 — 添加授權認證

在這個實驗中剂碴,我們將會使得 GetView 方法更加安全把将。我們將會確保只有合法用戶才能訪問行為方法。

在第一天的系列學習中忆矛,我們了解了 ASP.NET 和 MVC 的真實含義察蹲。我們理解到 ASP.NET MVC 是 ASP.NET 的一部分请垛。許多 ASP.NET 的功能都被 ASP.NET MVC 所繼承。其中一個功能便是表單的認證洽议。

在我們開始實驗之前宗收,我們先了解下在 ASP.NET 中如何進行表單認證工作。

  1. 終端用戶通過瀏覽器發(fā)送一個表單認證的請求亚兄。

  2. 瀏覽器將發(fā)送所有請求相關的Cookies混稽,存儲在客戶機器上。

  3. 當請求被服務器端接收到审胚,服務器會檢查請求并檢查特殊的 Cookie匈勋,即「Authentication Cookie」。

  4. 如果發(fā)現(xiàn)了合法的認證 Cookie膳叨,服務器就會證實用戶的身份颓影,或者簡單來說,認為用戶是一個合法的用戶懒鉴,并允許他進行下一步诡挂。

  5. 如果沒有發(fā)現(xiàn)合法的認證 Cookie,服務器就會認為用戶是匿名(未認證)的用戶临谱。在這種情況下璃俗,如果被請求的資源被標記為 Protected/Secured,那么用戶將會被重新導向到登錄頁面悉默。

第一步:創(chuàng)建一個 AuthenticationController 和一個登錄行為方法

右擊 Controller 文件夾城豁,選擇「Add New Controller」,然后創(chuàng)建一個控制器抄课,命名為「Authentication」唱星。在這種情況下,全名應該為「AuthenticationController」跟磨。

在控制器內創(chuàng)建一個行為方法间聊,命名為 Login。

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

第二步:創(chuàng)建 Model

在 Model 文件夾下創(chuàng)建一個 Model 類抵拘,命名為 UserDetails哎榴。

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

第三步:創(chuàng)建 Login 視圖

在「~/Views/Authentication」文件夾下創(chuàng)建一個新的視圖,命名為 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。

  • 在視圖中我們已經創(chuàng)建了一個 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>

第四步:執(zhí)行并測試

按下 F5,執(zhí)行應用相速。在地址欄輸入 Login 方法的 URL碟渺。在這個例子中,將會是「http://localhost:8870/Authentication/Login」突诬。

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

第五步:授權表單認證

打開 Web.config 文件苫拍。導航到 System.Web 區(qū)域。找到子區(qū)域 Authentication旺隙。如果這里不存在就創(chuàng)建一個绒极。設置 Authentication 的模式是 Forms,Login URL 指向第一步所創(chuàng)建的「Login」的行為方法蔬捷。

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

第六步:讓行為方法更加安全

打開 EmployeeController垄提,然后向 Index 方法附上 Authorize 屬性。

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

第七步:執(zhí)行并測試

按下 F5 并執(zhí)行應用周拐。在地址欄中輸入 EmployeeController 的 Index 行為方法的 URL铡俐。在這個情景中,URL 為“http://localhost:8870/Employee/Index”妥粟。

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

正如你所看見的审丘,Index 行為的請求將會重指向 Login 行為。

第八步:創(chuàng)建一個業(yè)務層功能

打開 EmployeeBusinessLayer 類勾给,然后創(chuàng)建一個方法叫做 IsValidUser滩报。

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

注:在業(yè)務層,我們對比 UserName 和 Password 的硬編碼值播急。在真實場景中脓钾,我們能夠調用數(shù)據(jù)庫層,并對比真實的值桩警。

第九步:創(chuàng)建 DoLogin 行為方法

打開 AuthenticationController 類惭笑,然后創(chuàng)建一個新的行為方法,叫做 DoLogin生真。

這個 DoLogin 方法會在 Login 按鈕點擊時被觸發(fā)沉噩。

現(xiàn)在我們來列舉一下 DoLogin 中需要做的幾點。

  1. 通過觸發(fā)業(yè)務層功能來檢查用戶的合法性柱蟀。

  2. 如果用戶是一個合法用戶川蒙,就創(chuàng)建一個認證的 Cookie。它能夠使得未來的請求是認證過的請求长已。

  3. 如果用戶是不合法的畜眨,就向當前的 ModelState 增加一個錯誤昼牛。這個錯誤將會呈現(xiàn)在視圖中。

    [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");
    }
    }

讓我們理解下上述代碼區(qū)域康聂。

  • 如果你記得「第三天 — Lab 13」中我們所談論的 ModelState贰健,那么就能理解它包含了模型的狀態(tài)。它包含了當前模型的錯誤信息恬汁。在上述的代碼片段中伶椿,當用戶是一個非法用戶時,就增加一個新的錯誤氓侧。(錯誤由鍵值「CredentialError」和信息「Invalid UserName or Password」組成)

  • 表單認證脊另。SetAuthCookie 將會在客戶機器上創(chuàng)建一個新的 Cookie。

第十步:在視圖中呈現(xiàn)信息

打開 Login 視圖约巷,在 @Html.BeginForm 上增加一小段代碼偎痛。

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

第十一步:執(zhí)行并測試

按下 F5 并執(zhí)行應用。直切發(fā)起一個指向 Login Action 的請求独郎。我相信你現(xiàn)在已經知道如何去做踩麦。

注:如果你想發(fā)起一個指向 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。作為一個最佳實踐鬼譬,每一個行為方法都應該被附上這樣的屬性娜膘。

注意:為了保持代碼和學習是輕松并簡單的,我們在這個系列學習中并沒有全部都遵循最佳實踐优质,但是我們建議你在項目中遵循竣贪。

在我們持續(xù)的學習中,我們將會繼續(xù)談論最佳實踐巩螃。

FormsAuthentication.SetAuthCookie 是必須要寫的嗎演怎?

答案是肯定的。

讓我們來理解一個小的程序避乏。

  • 用戶通過瀏覽器向服務器發(fā)送請求爷耀。

  • 當請求通過瀏覽器發(fā)出,所有相關聯(lián)的 Cookies 將會伴隨著請求拍皮。

  • 服務器接收到請求并且準備發(fā)出響應歹叮。

  • 現(xiàn)在我們已經知道請求和響應是通過 HTTP 協(xié)議跑杭,并且 HTTP 是無關國籍的。對于服務器而言咆耿,每一個請求都是一個新的請求德谅,因此當相同的用戶發(fā)出兩次請求時,服務器并不能夠識別它萨螺。為了解決這個問題窄做,服務器在準備響應的時候增加了 Cookie,然后返回響應屑迂。

  • 當客戶端瀏覽器接收到伴隨著 Cookie 的響應后,它將會在客戶端創(chuàng)建 Cookies冯键。

  • 現(xiàn)在客戶又一次發(fā)出請求惹盼,服務器將會識別他,因為請求中包含了 Cookies惫确。

FormsAuthentication.SetAuthCookie 將會向響應增加一個特殊的 Cookie手报,命名為「Authentication」。

如果沒有 Cookies,是不是意味著 FormsAuthentication 就不會起作用?

答案是否定的亲善。我們有替代物赖临。我們可以運用 URI,而不是 Cookies流强。

打開 Web.config,然后更改「Authentication/Forms」區(qū)域如下。

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

正如你所看見的阳藻,現(xiàn)在認證 Cookie 通過 URL 它自己傳輸。

默認情況下谈撒,Cookieless 屬性會被設置為「AutoDetect」腥泥。這意味著認證工作通過 Cookie 完成,在這種情景下啃匿,Cookies 將不會支持通過 URL 傳輸蛔外。

FormsAuthentication.SetAuthCookie 的第二個參數(shù)起到什么作用?

這決定了我們是否愿意創(chuàng)建一個永久的 Cookie溯乒。非永久的 Cookie 將會在瀏覽器關閉時自動刪除夹厌。永久性的 Cookies 將不會被自動刪除。我們可以通過代碼或者瀏覽器設置手動刪除裆悄。

通過代碼做到登出尊流?

我們將會在以后的 Lab 中學習。

當認證失敗時灯帮,UserName 文本框將會如何被填充崖技?

這是 HTML Helper 類的神奇之處逻住。它們將會在控件中填充來自 Posted 數(shù)據(jù)的值。這是運用 HTML Helper 類的其中一個優(yōu)勢迎献。

@Html.ValidationMessage 是做什么的瞎访?

我們在 Lab 13 中已經探討過了。它根據(jù)鍵值呈現(xiàn) ModelState 的錯誤吁恍。

Authorize 屬性是做什么的扒秸?

在 ASP.NET MVC 中,存在一個概念叫做 Filters冀瓦。運用它可以過濾請求和響應伴奥。這里存在四種過濾器。我們將會在第七天的學習中探討它們翼闽。Authorize 屬性通過 Authorization 過濾器得出拾徙。這樣可以確保只有認證過的請求允許指向動作方法。

我們可以在一個行為方法中同時附上 HttpPost 和 Authorize 屬性嗎感局?

答案是肯定的尼啡。

在這個例子中,為什么沒有 ViewModel询微?

在 Lab 6 中我們所探討的崖瞭,View 應該和 Model 直接相關聯(lián)。我們必須有 View 和 Model 中的 ViewModel撑毛。視圖是展示視圖或者數(shù)據(jù)入口視圖都不重要书聚,它們都應該和 ViewModel 相關聯(lián)。在真實項目中藻雌,我強烈希望你全部都運用上 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
{

第四步:執(zhí)行并測試應用

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

我們將會在日后的試驗中探討這個問題的細節(jié)再悼。

為什么 AuthenticationController 中需要加上 AllowAnonymous 屬性?

我們已經附上了 Global 級別的 Authorize 過濾器膝但。這意味著所有的方法都將是受保護的冲九,包括 Login 和 DoLogin 行為方法。AllowAnonymous 為未認證的請求開啟了行為方法。

FilterConfig 類中的 RegisterGlobalFilters 方法如何被觸發(fā)的莺奸?

它通過 Global.asax 文件內的 Application_Start 事件所觸發(fā)丑孩。

4. Lab 18 — 在視圖中展示 UserName

在本實驗中,我們將在視圖中呈現(xiàn)當前登錄的用戶名灭贷。

第一步:在 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
......

第三步:在視圖中呈現(xiàn) UserName

打開 Index.cshtml 視圖览妖,呈現(xiàn) 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>

第四步:執(zhí)行并測試

按下 F5 并執(zhí)行應用。完成登錄操作后讽膏,你將會看到如下輸出檩电。

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

5. Lab 19 — 實現(xiàn)登出

第一步:創(chuàng)建登出鏈接

打開 Index.cshtml 文件然后創(chuàng)建登出鏈接。

<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">

第二步:創(chuàng)建登出行為方法

打開 AuthenticationController桅打,然后增加一個行為方法是嗜,命名為 Logout愈案。

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

第三步:執(zhí)行并測試

按下 F5 并執(zhí)行應用挺尾。

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

6. Lab 20 — 在 Login 頁實現(xiàn)認證

第一步:增加數(shù)據(jù)注釋

打開 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; }
}

第二步:在視圖中呈現(xiàn)錯誤信息

改變 Login.cshtml遭铺,呈現(xiàn)錯誤信息。

@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");
    }
}

第四步:執(zhí)行并測試

按下 F5 并執(zhí)行應用

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

7. Lab 21 — 在 Login 頁實現(xiàn)客戶端的認證

這次我們將以不同的方式實現(xiàn)客戶端的認證涂召。

第一步:下載 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>

第三步:執(zhí)行并測試

按下 F5迫皱,執(zhí)行應用。

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

8. Lab 21 的 Q&A

客戶端的認證是如何實現(xiàn)的辖众?

正如你所看見的卓起,不費多少力氣就實現(xiàn)了客戶端的認證和敬。在 Login 視圖中,HTML 元素通過 HTML Helper 類產生既绩。Helper 函數(shù)能夠根據(jù)數(shù)據(jù)注釋屬性來生成帶有屬性附加的 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」文件使用饲握,因此在客戶端自動實現(xiàn)認證私杜。

自動的客戶端認證是 HTML Helper 類的第二個優(yōu)勢。

Unobtrusive JavaScript 的意思是什么救欧?

下面是 Wikipedia 所述衰粹。譯文為:

Unobtrusive JavaScript 是一個在 Web 頁面中常用的 JavaScript 方法。盡管它沒有被正式定義笆怠,但是它的幾個基本的準則可以被大概理解:

  • 從 Web 頁面的結構或者內容中分離的功能("Behaviour Layer")铝耻,并且展示。

  • 傳統(tǒng)的 JavaScript 編程中蹬刷,最佳避免問題的實踐瓢捉。(例如瀏覽器的不一致性和缺乏伸縮性)

  • 逐步增強支持用戶可能不先進的 JavaScript 功能。

讓我以外行來定義一下办成。

「以一種方式來寫你的 JavaScript泡态,這種 JavaScript 不能與 HTML 強聯(lián)系。JavaScript 可能訪問 DOM 元素迂卢,JavaScript 可能操作 DOM 元素某弦,但是并不和它們直接聯(lián)系《耍」

在上述例子中靶壮,JQuery Unobtrusive JavaScript 簡單地運用一些輸入元素屬性和實現(xiàn)客戶端認證。

我們可以運用這些 JavaScript 來認證员萍,而不是采用 HTML Helper 類嗎腾降?

答案是肯定的,對于這種方式碎绎,我們需要手動地向元素附件屬性螃壤。

哪個更推崇,是 HTML Helper 函數(shù)還是純 HTML混卵?

我個人更傾向于純 HTML映穗,因為 HTML Helper 函數(shù)又一次用到了「Full Control over HTML」,而這個弊端我們曾討論過幕随。

其次我們討論下使用 JavaScript 框架或庫蚁滋,而不是使用 JQuery 的項目情況。一些框架例如 Angular。在這種情況下辕录,大多數(shù)我們都會考慮 Angular 認證睦霎,并且自定義的 HTML 認證屬性將會拋棄。

9. 總結

現(xiàn)在我們已經完成了第四天的學習走诞。在第五天中副女,我們將會有更進一步的學習,會更有樂趣蚣旱。

原文地址:Learn MVC Project in 7 days

本文系 OneAPM 工程師編譯整理碑幅。OneAPM 是應用性能管理領域的新興領軍企業(yè),能幫助企業(yè)用戶和開發(fā)者輕松實現(xiàn):緩慢的程序代碼和 SQL 語句的實時抓取塞绿。想閱讀更多技術文章沟涨,請訪問 OneAPM 官方博客

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末异吻,一起剝皮案震驚了整個濱河市裹赴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诀浪,老刑警劉巖棋返,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雷猪,居然都是意外死亡睛竣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門春宣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酵颁,“玉大人嫉你,你說我怎么就攤上這事月帝。” “怎么了幽污?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵嚷辅,是天一觀的道長。 經常有香客問我距误,道長簸搞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任准潭,我火速辦了婚禮趁俊,結果婚禮上,老公的妹妹穿的比我還像新娘刑然。我一直安慰自己寺擂,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著怔软,像睡著了一般垦细。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挡逼,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天括改,我揣著相機與錄音,去河邊找鬼家坎。 笑死嘱能,一個胖子當著我的面吹牛,可吹牛的內容都是我干的虱疏。 我是一名探鬼主播焰檩,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼订框!你這毒婦竟也來了析苫?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤穿扳,失蹤者是張志新(化名)和其女友劉穎衩侥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矛物,經...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡茫死,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了履羞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峦萎。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖忆首,靈堂內的尸體忽然破棺而出爱榔,到底是詐尸還是另有隱情,我是刑警寧澤糙及,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布详幽,位于F島的核電站,受9級特大地震影響浸锨,放射性物質發(fā)生泄漏唇聘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一柱搜、第九天 我趴在偏房一處隱蔽的房頂上張望迟郎。 院中可真熱鬧,春花似錦聪蘸、人聲如沸宪肖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匈庭。三九已至夫凸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阱持,已是汗流浹背夭拌。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留衷咽,地道東北人鸽扁。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像镶骗,于是被迫代替她去往敵國和親桶现。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內容