1. 程式人生 > >ASP.NET Core 中文文件 第二章 指南(4.6)Controller 方法與檢視

ASP.NET Core 中文文件 第二章 指南(4.6)Controller 方法與檢視

我們已經初步的建立了一個 movie 應用程式,但是展示並不理想。我們不希望看到 release date 欄位顯示時間並且 ReleaseDate 應該是兩個單詞。
movies-Index

開啟 Models/Movie.cs 檔案並新增下面高亮的程式碼行:

public class Movie
{
    public int ID { get; set; }
    public string Title { get; set; }

    [Display(Name = "Release Date")] //手動高亮
    [DataType(DataType.Date)]        //手動高亮
    public DateTime ReleaseDate { get; set; }
    public string Genre { get; set; }
    public decimal Price { get; set; }
}
  • 右鍵點選紅色波浪線程式碼行 > Quick Actions
    Quick Actions

  • 點選 using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations;

Visual studio 會自動新增 using System.ComponentModel.DataAnnotations; 引用程式碼。

讓我們移除多餘的 using 引用程式碼。它們預設以灰色字型出現。右鍵點選 Movie.cs 檔案 點選 > Organize Usings > Remove Unnecessary Usings 選單。
Remove Unnecessary Usings
更新後的程式碼:

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }
        public string Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

我們會在下一篇文章中繼續發掘 DataAnnotations 的內容。Display 特性用來指定欄位的顯示名 (在本示例中 “Release Date” 會替代 “ReleaseDate”)。DataType 特性指定資料型別,在本示例是日期型別,所以欄位中儲存的時間資訊不會被顯示。

瀏覽 Movies 控制器並把滑鼠懸停於 Edit 連結上可以看到目標 URL。
Movies-Edit

EditDetails 以及 Delete 連結是由 Views/Movies/Index.cshtml 檔案中的 MVC Core Anchor Tag Helper 自動生成的。

<td>
    <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |         //手動高亮
    <a asp-action="Details" asp-route-id="@item.ID">Details</a> |   //手動高亮
    <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>       //手動高亮
</td>

Tag Helpers 允許伺服器端程式碼在 Razor 檔案中建立和生成 HTML 元素。在上面的程式碼中,AnchorTagHelper通過 controller 方法以及路由ID 動態生成 HTML href 屬性值。你可以在你熟悉的瀏覽器中使用 View Source 選單或者使用 F12 工具來檢查你生成的 HTML 標籤。 F12 工具如下圖。
F12工具

在 Startup.cs 檔案中設定回撥路由格式。

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");  //手動高亮
});

ASP.NET Core 會把 http://localhost:1234/Movies/Edit/4 轉化成傳送到 Movies controller 的 Edit 方法的請求並帶上值為 4 的 ID 引數。(Controller 方法其實就是指代 action 方法。)

Tag Helpers 是 ASP.NET Core 中最受歡迎的新功能之一。 參考 附錄資源 獲取更多資訊。

開啟 Movies controller 並檢視兩個 Edit 方法:
兩個 Edit 方法

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}
// POST: Movies/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Genre,Price,ReleaseDate,Title")] Movie movie)
{
    if (id != movie.ID)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.ID))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction("Index");
    }
    return View(movie);
}

[Bind] 特性是防止 over-posting (過度提交,客戶端可能傳送比期望還多的資料,比如只需要2個屬性但是傳送了3個屬性)的一種方法。你應該只把需要改變的屬性包含到 [Bind] 特性中。請參閱 Protect your controller from over-posting 獲取更多資訊,ViewModels 提供了另一種防止 over-posting 的方法。

請注意帶第二個 Edit 方法被 [HttpPost] 特性所修飾。

[HttpPost] //手動高亮
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Genre,Price,ReleaseDate,Title")] Movie movie)
{
    if (id != movie.ID)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.ID))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction("Index");
    }
    return View(movie);
}

[HttpPost]特性指定這個 Edit 方法 只能 被 POST 請求呼叫。你可以把 [HttpGet] 特性應用到第一個 edit 方法,但是,不是必須的,因為 [HttpGet] 是被預設使用的。

[ValidateAntiForgeryToken] 特性是用來防止偽造請求的,會在(Views/Movies/Edit.cshtml)檢視最終呈現檔案中加入反偽造標記和伺服器進行配對。edit 檢視生成反偽造標記請參考 Form Tag Helper

<form asp-action="Edit">

Form Tag Helper 生成一個隱藏域的防偽標記必須和 Movies controller 的 Edit 方法的 [ValidateAntiForgeryToken] 產生的防偽標記相匹配。更多資訊請參考 Anti-Request Forgery

HttpGet Edit 方法獲取 movie 的 ID 引數,通過使用 Entity Framework 的 SingleOrDefaultAsync 方法查詢 movie,並將選中的 movie 填充到 Edit 檢視。如果 movie 沒有找到,返回 NotFound (HTTP 404) 響應。

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

在基架系統建立 Edit 檢視的時候,會檢查 Movie 類併為它的每個屬性生成程式碼以呈現 <label> 和 <input> 元素。下面的例子展示了 Visual Studio 基架系統生成的 Edit 檢視:

@model MvcMovie.Models.Movie //手動高亮

@{
    ViewData["Title"] = "Edit";
}

<h2>Edit</h2>

<form asp-action="Edit">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="ID" />
        <div class="form-group">
            <label asp-for="Genre" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Price" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="ReleaseDate" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Title" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
</form>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

你會注意到為什麼檢視模版檔案的頂部會有一行 @model MvcMovie.Models.Movie 宣告呢?— 因為這個宣告指定這個檢視模版的模型期待的型別是 Movie

基架生成的程式碼使用幾個 Tag Helper 方法來簡化 HTML 標記。 Label Tag Helper 用來顯示欄位名(“Title”、”ReleaseDate”、”Genre” 或者 “Price”)。Input Tag Helper 用來呈現 HTML <input> 元素。Validation Tag Helper 顯示關聯到屬性的錯誤資訊。

執行應用程式並導航到 /Movies URL。單擊 編輯 連結。在瀏覽器中檢視該頁面的原始碼。為 <form> 元素生成的 HTML 如下所示。

<form action="/Movies/Edit/7" method="post"> //手動高亮
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        <div class="text-danger" />
        <input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />  //手動高亮
        <div class="form-group">
            <label class="control-label col-md-2" for="Genre" />
            <div class="col-md-10">
                <input class="form-control" type="text" id="Genre" name="Genre" value="Western" />  //手動高亮
                <span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true" />
            </div>
        </div>
        <div class="form-group">
            <label class="control-label col-md-2" for="Price" />
            <div class="col-md-10">
                <input class="form-control" type="text" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" value="3.99" /> //手動高亮
                <span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true" />
            </div>
        </div>
        <!-- Markup removed for brevity -->
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" /> //手動高亮
            </div>
        </div>
    </div>
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" /> //手動高亮
</form>

HTML <form> 中的 <input> 元素的 action 屬性用於設定請求傳送到 /Movies/Edit/id URL。當點選 Save 按鈕時表單資料會被髮送到伺服器。在 </form> 元素關閉前最後一行 </form> 展示了 XSRF 生成的隱藏域標識。

處理 POST 請求

下面的列表顯示了 [HttpPost] 不同版本的 Edit 方法。

[HttpPost]  //手動高亮
[ValidateAntiForgeryToken]  //手動高亮
public async Task<IActionResult> Edit(int id, [Bind("ID,Genre,Price,ReleaseDate,Title")] Movie movie)
{
    if (id != movie.ID)
    {
        return NotFound();
    }

    if (ModelState.IsValid)  //手動高亮
    {
        try
        {
            _context.Update(movie);  //手動高亮
            await _context.SaveChangesAsync();  //手動高亮
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.ID))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction("Index");  //手動高亮
    }
    return View(movie);
}

[ValidateAntiForgeryToken] 特性驗證 Form Tag Helper 生成的存放在隱藏域中的 XSRF 反偽造標記。

模型繫結機制以傳送表單資料建立 Movie 物件並作為 movie 引數。ModelState.IsValid 方法驗證表單提交的資料可以用來修改(編輯或更新)一個 Movie 物件。如果資料有效,就可以儲存。更新(編輯) movie 資料會被存到資料庫通過 database context 的 SaveChangesAsync 方法。資料儲存完畢以後,這段程式碼將使用者重定向到 MoviesController 類的 Index 方法,這個頁面顯示了改動後最新的Movie集合。

表單資料被髮布到伺服器之前,客戶端校驗會檢查所有欄位上的驗證規則。如果有任何驗證錯誤,則顯示錯誤訊息,並且表單資料不會被髮送。如果禁用了 JavaScript,將不會有客戶端驗證,但伺服器端將檢測出傳送資料是無效的,表單依舊會顯示出錯誤資訊。在稍後的教程中,我們會探討 Model Validation 模型驗證 更多關於驗證的細節。Views/Book/Edit.cshtml 檢視模版中的 Validation Tag Helper 負責顯示錯誤資訊。
Model Validation 模型驗證

movie controller 的所有 HttpGet 方法都遵循類似的模式。它們獲取一個物件(或者物件列表,比如 Index),把物件(模型)傳遞到檢視。Create 方法建立一個空的物件到 Create 檢視。諸如 Create、Edit、Delete 等之類的會修改資料的方法都會在 [HttpPost] 版本的過載方法中這樣做(譯者注:執行類似於前文所述的這些操作)。在 HTTP GET 方法中修改資料有安全風險,參考 ASP.NET MVC 提示 #46 – 不要使用刪除連結,因為他們製造安全漏洞 。在 HTTP GET 方法中修改資料同樣也違反 HTTP 最佳實踐以及 REST 架構模式,其中規定 GET 請求不應該更改應用程式的狀態。換句話說,執行 GET 操作應該是沒有任何副作用,不會修改您的持久化的資料。

附錄資源

返回目錄

相關推薦

ASP.NET Core 中文 第二 指南4.6Controller 方法檢視

我們已經初步的建立了一個 movie 應用程式,但是展示並不理想。我們不希望看到 release date 欄位顯示時間並且 ReleaseDate 應該是兩個單詞。 開啟 Models/Movie.cs 檔案並新增下面高亮的程式碼行: public class Movie { public in

ASP.NET Core 中文 第二 指南4.10檢查自動生成的Detail方法和Delete方法

開啟 Movie 控制器並檢視 Details 方法: // GET: Movies/Details/5 public async Task<IActionResult> Details(int? id) { if (id == null) { return No

ASP.NET Core 中文 第二 指南4.1ASP.NET Core MVC Visual Studio 入門

這篇教程將告訴你如何使用 Visual Studio 2015 構建一個 ASP.NET Core MVC Web 應用程式的基礎知識。 安裝 Visual Studio 和 .NET Core 安裝 Visual Studio Community 2015。選擇 Community 下載並執行預設安裝

ASP.NET Core 中文 第二 指南4.5使用 SQL Server LocalDB

ApplicationDbContext 類負責連線資料庫並將 Movie 物件和資料記錄進行對映。 Startup.cs 檔案中,資料庫上下文是在 ConfigureServices 方法中用 Dependency Injection 容器進行註冊的。 // This method gets called

ASP.NET Core 中文 第二 指南1用 Visual Studio Code 在 macOS 上建立首個 ASP.NET Core 應用程式

本文已更新,最後更新於2017年4月28日 聯絡我們: QQ Group: 436035237 (dotNet Core Studying Group) GitHub Repo: https://github.com/dotnetcore/aspnetcore-doc-cn/ 以下為老翻譯存檔 本節將

ASP.NET Core 中文 第二 指南2用 Visual Studio 和 ASP.NET Core MVC 建立首個 Web API

HTTP 協議不僅僅提供網頁服務。它也是一個構建公開服務和資料 API 的強大平臺。HTTP 協議是簡單、靈活、無處不在的。幾乎你能想到的任何平臺上都有 HTTP 支援,所以 HTTP 服務能夠傳送到多種客戶端, 包括瀏覽器,移動裝置和傳統的桌面應用程式。 在本教程中,你將建立一個簡單的 Web API 來

ASP.NET Core 中文 第二 指南 09 使用 Swagger 生成 ASP.NET Web API 線上幫助測試

對於開發人員來說,構建一個消費應用程式時去了解各種各樣的 API 是一個巨大的挑戰。 在你的 Web API 專案中使用 Swagger 的 .NET Core 封裝 Swashbuckle 可以幫助你建立良好的文件和幫助頁面。 Swashbuckle 可以通過修改 Startup.cs 作為一組 NuGe

ASP.NET Core 中文 第二 指南8 使用 dotnet watch 開發 ASP.NET Core 應用程式

本文已更新,最後更新於2017年4月27日 以下為老翻譯存檔 介紹 dotnet watch 是一個開發階段在原始檔發生變動的情況下使用 dotnet 命令的工具。 當代碼發生變動的時候可以用來執行編譯,執行測試,或者釋出操作。 在本教程中,我們將使用一個現有的計算兩個數字之和以及乘積的 WebAp

ASP.NET Core 中文 第二 指南5 在 Nano Server 上執行ASP.NET Core

注意:本教程使用 Windows Server Technical Preview 5 的預發行版本的 Nano Server 安裝選項。 你可以在虛擬硬碟映像中用來內部演示和評估,但不能在生產環境中使用該軟體。可通過 https://go.microsoft.com/fwlink/?LinkId=624

ASP.NET Core 中文 第二 指南3用 Visual Studio 釋出一個 Azure 雲 Web 應用程式

設定開發環境 注意 如果你的機器之前任何依賴都沒有安裝過,SDK 的安裝時間將會超過30分鐘。 建立一個 Web 應用程式 在 Visual Studio 的 Start 頁面,點選 New Project。 另外,你也可以通過選單新建專案。點選 File > New > Proje

ASP.NET Core 中文 第四 MVC4.6Areas區域

Areas 是 ASP.NET MVC 用來將相關功能組織成一組單獨名稱空間(路由)和資料夾結構(檢視)的功能。使用 Areas 建立層次結構的路由,是通過新增另一個路由引數 area 到 Controller 和 action。 Areas 提供了一種把大型 ASP.NET Core MVC Web 應用

ASP.NET Core 中文 第五 測試5.2整合測試

整合測試確保應用程式的元件組裝在一起時正常工作。 ASP.NET Core支援使用單元測試框架和可用於處理沒有網路開銷請求的內建測試的網路主機整合測試。 章節: 整合測試介紹 整合測試驗證應用程式不同的部位是否正確地組裝在一起。不像單元測試,整合測試經常涉及到應用基礎設施,如資料庫,檔案系統,網路資源

ASP.NET Core 中文 第四 MVC4.2控制器操作的路由

ASP.NET Core MVC 使用路由 中介軟體 來匹配傳入請求的 URL 並對映到具體的操作。路由通過啟動程式碼或者特性定義。路由描述 URL 路徑應該如何匹配到操作。路由也同樣用於生成響應中返回的 URL(用於連結)。 這篇文章將解釋 MVC 和路由之間的相互作用,以及典型的 MVC 應用程式如何使

ASP.NET Core 中文 第四 MVC4.1Controllers, Actions 和 Action Results

Action 和 action result 是開發者使用 ASP.NET MVC 構建應用程式的基礎部分。 什麼是 Controller 在 ASP.NET MVC 中, 控制器( Controller  )用於定義和聚合操作(Action)的一個集合。操作( 或操作方法 )是控制器中處理入站請求的一個方

ASP.NET Core 中文 第四 MVC2.3格式化響應資料

ASP.NET Core MVC 內建支援對相應資料(response data)的格式化,用來修正格式或生成客戶端指定的格式。 特定格式的操作結果 某些操作結果(Action result)的型別是指定的特定格式,比如 JsonResult 或 ContentResult。Action 可以返回格式化為

ASP.NET Core 中文 第四 MVC4.4依賴注入和控制器

ASP.NET Core MVC 控制器應通過它們的構造器明確的請求它們的依賴關係。在某些情況下,單個控制器的操作可能需要一個服務,在控制器級別上的請求可能沒有意義。在這種情況下,你也可以選擇將服務作為 action 方法的引數。 章節: 依賴注入 依賴注入(Dependency injection,

ASP.NET Core 中文 第四 MVC01ASP.NET Core MVC 概覽

ASP.NET Core MVC 是使用模型-檢視-控制器(Model-View-Controller)設計模式構建網頁應用與 API 的豐富的框架。 什麼是 MVC 模式? 模型-檢視-控制器(MVC)架構模式將一個應用區分為三部分主要元件:模型、檢視、與控制器。這種模式有助實現關注分離。使用這種模式,使

ASP.NET Core 中文 第四 MVC4.3過濾器

ASP.NET MVC 過濾器 可在執行管道的前後特定階段執行程式碼。過濾器可以配置為全域性有效、僅對控制器有效或是僅對 Action 有效。 過濾器如何工作? 不同的過濾器型別會在執行管道的不同階段執行,因此它們各自有一套適用場景。根據你實際要解決的問題以及在請求管道中執行的位置來選擇建立不同的過濾器。

ASP.NET Core 中文 第三 原理2中介軟體

章節: 什麼是中介軟體 中介軟體是用於組成應用程式管道來處理請求和響應的元件。管道內的每一個元件都可以選擇是否將請求交給下一個元件、並在管道中呼叫下一個元件之前和之後執行某些操作。請求委託被用來建立請求管道,請求委託處理每一個 HTTP 請求。 請求委託通過使用 IApplicationBuilder

ASP.NET Core 中文 第四 MVC3.8檢視中的依賴注入

ASP.NET Core 支援在檢視中使用 依賴注入 。這將有助於提供檢視專用的服務,比如本地化或者僅用於填充檢視元素的資料。你應該儘量保持控制器和檢視間的關注點分離(separation of concerns)。你的檢視所顯示的大部分資料應該從控制器傳入。 章節: 一個簡單的示例 你可以使用 @i