ASP.NET Core 中的控制器方法和視圖

2021-06-15 15:37 更新

電影應(yīng)用的開頭不錯(cuò),但展示效果不理想,例如,ReleaseDate 應(yīng)為兩個(gè)詞。

ASP.NET Core 中的控制器方法和視圖

打開 Models/Movie.cs 文件,并添加以下代碼中突出顯示的行:

C#

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

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

        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }
    }
}

下一教程將介紹 DataAnnotations。 Display 特性指定要顯示的字段名稱的內(nèi)容(本例中應(yīng)為“Release Date”,而不是“ReleaseDate”)。 DataType 屬性指定數(shù)據(jù)的類型(日期),使字段中存儲(chǔ)的時(shí)間信息不會(huì)顯示。

要使 Entity Framework Core 能將 Price 正確地映射到數(shù)據(jù)庫(kù)中的貨幣,則必須使用 [Column(TypeName = "decimal(18, 2)")] 數(shù)據(jù)注釋。 有關(guān)詳細(xì)信息,請(qǐng)參閱數(shù)據(jù)類型。

瀏覽到 Movies 控制器,并將鼠標(biāo)指針懸停在“編輯”鏈接上以查看目標(biāo) URL。

鼠標(biāo)懸停在“編輯”鏈接上的瀏覽器窗口,顯示了 https://localhost:5001/Movies/Edit/5 的鏈接 URL

“編輯”、“詳細(xì)信息”和“刪除”鏈接是在 Views/Movies/Index.cshtml 文件中由 Core MVC 定位標(biāo)記幫助程序生成的 。

CSHTML

        <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>
</tr>

標(biāo)記幫助程序使服務(wù)器端代碼可以在 Razor 文件中參與創(chuàng)建和呈現(xiàn) HTML 元素。 在上面的代碼中,AnchorTagHelper 從控制器操作方法和路由 ID 動(dòng)態(tài)生成 HTML href 特性值。在最喜歡的瀏覽器中使用“查看源”,或使用開發(fā)人員工具來(lái)檢查生成的標(biāo)記。 生成的 HTML 的一部分如下所示:

HTML

 <td>
    <a href="/Movies/Edit/4"> Edit </a> |
    <a href="/Movies/Details/4"> Details </a> |
    <a href="/Movies/Delete/4"> Delete </a>
</td>

重新調(diào)用在 Startup.cs 文件中設(shè)置的路由的格式:

C#

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

ASP.NET Core 將 https://localhost:5001/Movies/Edit/4 轉(zhuǎn)換為對(duì) Movies 控制器的 Edit 操作方法的請(qǐng)求,參數(shù) Id 為 4。 (控制器方法也稱為操作方法。)

標(biāo)記幫助程序是 ASP.NET Core 中最受歡迎的新功能之一。 有關(guān)詳細(xì)信息,請(qǐng)參閱其他資源。

打開 Movies 控制器并檢查兩個(gè) Edit 操作方法。 以下代碼顯示了 HTTP GET Edit 方法,此方法將提取電影并填充由 Edit.cshtml Razor 文件生成的編輯表單。

C#

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

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

以下代碼顯示 HTTP POST Edit 方法,它會(huì)處理已發(fā)布的電影值:

C#

// 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,Title,ReleaseDate,Genre,Price")] 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] 特性是防止過(guò)度發(fā)布的一種方法。 只應(yīng)在 [Bind] 特性中包含想要更改的屬性。 有關(guān)詳細(xì)信息,請(qǐng)參閱防止控制器過(guò)度發(fā)布。 ViewModels 提供了一種替代方法以防止過(guò)度發(fā)布。

請(qǐng)注意第二個(gè) Edit 操作方法的前面是 [HttpPost] 特性。

C#

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] 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(nameof(Index));
    }
    return View(movie);
}

HttpPost 特性指定只能為 POST 請(qǐng)求調(diào)用此 Edit 方法。 可將 [HttpGet] 屬性應(yīng)用于第一個(gè)編輯方法,但不是必需,因?yàn)?nbsp;[HttpGet] 是默認(rèn)設(shè)置。

ValidateAntiForgeryToken 特性用于防止請(qǐng)求偽造,并與編輯視圖文件 (Views/Movies/Edit.cshtml) 中生成的防偽標(biāo)記相配對(duì)。 編輯視圖文件使用表單標(biāo)記幫助程序生成防偽標(biāo)記。

CSHTML

<form asp-action="Edit">

表單標(biāo)記幫助程序會(huì)生成隱藏的防偽標(biāo)記,此標(biāo)記必須與電影控制器的 Edit 方法中 [ValidateAntiForgeryToken] 生成的防偽標(biāo)記相匹配。 有關(guān)詳細(xì)信息,請(qǐng)參閱 防止跨站點(diǎn)請(qǐng)求偽造 (XSRF/CSRF) Core 中的 ASP.NET 攻擊。

HttpGet Edit 方法采用電影 ID 參數(shù),使用Entity Framework FindAsync 方法查找電影,并將所選電影返回到“編輯”視圖。 如果無(wú)法找到電影,則返回 NotFound (HTTP 404)。

C#

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

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

當(dāng)基架系統(tǒng)創(chuàng)建“編輯”視圖時(shí),它會(huì)檢查 Movie 類并創(chuàng)建代碼為類的每個(gè)屬性呈現(xiàn) <label> 和 <input> 元素。 以下示例顯示由 Visual Studio 基架系統(tǒng)生成的“編輯”視圖:

CSHTML

@model MvcMovie.Models.Movie

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

<h1>Edit</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

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

請(qǐng)注意視圖模板在文件頂端有一個(gè) @model MvcMovie.Models.Movie 語(yǔ)句。 @model MvcMovie.Models.Movie 指定視圖期望的視圖模板的模型為 Movie 類型。

基架的代碼使用幾個(gè)標(biāo)記幫助程序方法來(lái)簡(jiǎn)化 HTML 標(biāo)記。 標(biāo)簽標(biāo)記幫助程序顯示字段的名稱(“Title”、“ReleaseDate”、“Genre”或“Price”)。 輸入標(biāo)記幫助程序呈現(xiàn) HTML <input> 元素。 驗(yàn)證標(biāo)記幫助程序顯示與該屬性相關(guān)聯(lián)的任何驗(yàn)證消息。

運(yùn)行應(yīng)用程序并導(dǎo)航到 /Movies URL。 點(diǎn)擊“編輯”鏈接。 在瀏覽器中查看頁(yè)面的源。 為 <form> 元素生成的 HTML 如下所示。

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

<input> 元素位于 HTML <form> 元素中,后者的 action 特性設(shè)置為發(fā)布到 /Movies/Edit/id URL。 當(dāng)單擊 Save 按鈕時(shí),表單數(shù)據(jù)將發(fā)布到服務(wù)器。 關(guān)閉 </form> 元素之前的最后一行顯示表單標(biāo)記幫助程序生成的隱藏的 XSRF 標(biāo)記。

處理 POST 請(qǐng)求

以下列表顯示了 Edit 操作方法的 [HttpPost] 版本。

C#

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] 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(nameof(Index));
    }
    return View(movie);
}

[ValidateAntiForgeryToken] 特性驗(yàn)證表單標(biāo)記幫助程序中的防偽標(biāo)記生成器生成的隱藏的 XSRF 標(biāo)記

模型綁定系統(tǒng)采用發(fā)布的表單值,并創(chuàng)建一個(gè)作為 movie 參數(shù)傳遞的 Movie 對(duì)象。 ModelState.IsValid 屬性驗(yàn)證表單中提交的數(shù)據(jù)是否可以用于修改(編輯或更新)Movie 對(duì)象。 如果數(shù)據(jù)有效,將保存此數(shù)據(jù)。 通過(guò)調(diào)用數(shù)據(jù)庫(kù)上下文的 SaveChangesAsync 方法,將更新(編輯)的電影數(shù)據(jù)保存到數(shù)據(jù)庫(kù)。 保存數(shù)據(jù)后,代碼將用戶重定向到 MoviesController 類的 Index 操作方法,此方法顯示電影集合,包括剛才所做的更改。

在表單發(fā)布到服務(wù)器之前,客戶端驗(yàn)證會(huì)檢查字段上的任何驗(yàn)證規(guī)則。 如果有任何驗(yàn)證錯(cuò)誤,則將顯示錯(cuò)誤消息,并且不會(huì)發(fā)布表單。 如果禁用 JavaScript,則不會(huì)進(jìn)行客戶端驗(yàn)證,但服務(wù)器將檢測(cè)無(wú)效的發(fā)布值,并且表單值將與錯(cuò)誤消息一起重新顯示。 稍后在本教程中,我們將更詳細(xì)地研究模型驗(yàn)證。 Views/Movies/Edit.cshtml 視圖模板中的驗(yàn)證標(biāo)記幫助程序負(fù)責(zé)顯示相應(yīng)的錯(cuò)誤消息。

“編輯”視圖:不正確的“價(jià)格”值 abc 的異常,說(shuō)明“價(jià)格”字段必須是一個(gè)數(shù)字。 不正確的“發(fā)布日期”值 xyz 的異常,請(qǐng)輸入有效的日期。

電影控制器中的所有 HttpGet 方法都遵循類似的模式。 它們獲取電影對(duì)象(對(duì)于 Index獲取的是對(duì)象列表)并將對(duì)象(模型)傳遞給視圖。 Create 方法將空的電影對(duì)象傳遞給 Create 視圖。 在方法的 [HttpPost] 重載中,創(chuàng)建、編輯、刪除或以其他方式修改數(shù)據(jù)的所有方法都執(zhí)行此操作。 以 HTTP GET 方式修改數(shù)據(jù)是一種安全隱患。 以 HTTP GET 方法修改數(shù)據(jù)也違反了 HTTP 最佳做法和架構(gòu) REST 模式,后者指定 GET 請(qǐng)求不應(yīng)更改應(yīng)用程序的狀態(tài)。 換句話說(shuō),執(zhí)行 GET 操作應(yīng)是沒(méi)有任何隱患的安全操作,也不會(huì)修改持久數(shù)據(jù)。

其他資源

  • 全球化和本地化
  • 標(biāo)記幫助程序簡(jiǎn)介
  • 創(chuàng)作標(biāo)記幫助程序
  • 防止跨站點(diǎn)請(qǐng)求偽造 (XSRF/CSRF) Core 中的 ASP.NET 攻擊
  • 防止控制器過(guò)度發(fā)布
  • ViewModels
  • 表單標(biāo)記幫助程序
  • 輸入標(biāo)記幫助程序
  • 標(biāo)簽標(biāo)記幫助程序
  • 選擇標(biāo)記幫助程序
  • 驗(yàn)證標(biāo)記幫助程序


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)