.NET Magazine國際中文電子雜誌
作 者:許薰尹
文章編號: N201122402
出刊日期: 2020/11/25
這篇文章是《設計與使用ASP.NET Core Web API》系列的文章中的第三篇,延續《設計與使用ASP.NET Core Web API 1-使用Entity Framework Core存取資料》、《設計與使用ASP.NET Core Web API 2- Swagger》兩篇文章的情境,我們將使用ASP.NET Core Web API與Entity Framework Core設計CRUD功能。
查詢單一商店資料
在ASP.NET Core MVC「MyWeb」專案中,修改「HomeController」類別程式碼,加入一個「Details」方法,接收「id」當參數,在方法中建立「PublisherService」物件,並叫用「GetStoreByIdAsync」方法傳入「id」參數來叫用Web API取得編號相符的商店資料,請參考以下程式列表:
- MyWeb\Controllers\HomeController.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using MyWeb.Models;
namespace MyWeb.Controllers {
public class HomeController : Controller {
private readonly ILogger<HomeController> _logger;
private readonly IHttpClientFactory _httpClientFactory;
public HomeController( ILogger<HomeController> logger, IHttpClientFactory httpClientFactory ) {
_logger = logger;
_httpClientFactory = httpClientFactory;
}
public async Task<IActionResult> Index( ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
ICollection<Store> stores = await client.GetStoresAsync( );
return View( stores );
}
public async Task<IActionResult> Details( string id ) {
if ( id == null ) {
return NotFound( );
}
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
Store store = await client.GetStoreByIdAsync( id );
if ( store == null ) {
return NotFound( );
}
return View( store );
}
public IActionResult Privacy( ) {
return View( );
}
[ResponseCache( Duration = 0, Location = ResponseCacheLocation.None, NoStore = true )]
public IActionResult Error( ) {
return View( new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier } );
}
}
}
建立「Details」檢視
建立「Details」檢視。將游標停留在「HomeController」控制器程式設計畫面「Details」方法之中,按一下滑鼠右鍵,從快捷選單選取「Add View」,請參考下圖所示:

圖 1:建立「Details」檢視。
在「Add Razor View」對話盒中,設定以下項目:
- · 「View name」:「Details」。
- · 「Template」:「Details」。
- · 「Model class」:選取「Store」類別。
- · 勾選「Use a layout page」核取方塊。
然後按下「Add」按鈕。Visual Studio 2019便會在「Views\Home」資料夾下,新增一個「Details.cshtml」檔案,請參考下圖所示:

圖 2:建立「Details」檢視。
修改產生的「Details」檢視程式碼,修改「@Html.ActionLink」這行程式,第三個參數傳入一個匿名物件,指定要編輯的商店編號,以方便切換到資料編輯畫面,請參考以下程式列表:
- MyWeb\Views\Home\Details.cshtml
@model MyWeb.Store
@{
ViewData["Title"] = "Details";
}
<h1> Details </h1>
<div>
<h4> Store </h4>
<hr />
<dl class = "row">
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.StorId )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.StorId )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.StorName )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.StorName )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.StorAddress )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.StorAddress )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.City )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.City )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.State )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.State )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.Zip )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.Zip )
</dd>
</dl>
</div>
<div>
@Html.ActionLink( "Edit", "Edit", new { id = Model.StorId }) |
<a asp-action = "Index"> Back to List </a>
</div>
修改「Index」檢視程式碼,同樣在叫用「Html.ActionLink」方法產生「Details」超連結時,第三個參數傳入一個匿名物件,指定要編輯的商店編號,以方便從「Index」檢視切換到「Details」資料畫面:
Html.ActionLink( "Details", "Details", new { id item.StorId } )
目前「Index」檢視的程式碼看起來如下:
- MyWeb\Views\Home\Index.cshtml
@model IEnumerable<MyWeb.Store>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action = "Create"> Create New </a>
</p>
<table class = "table">
<thead>
<tr>
<th>
@Html.DisplayNameFor( model => model.StorId )
</th>
<th>
@Html.DisplayNameFor( model => model.StorName )
</th>
<th>
@Html.DisplayNameFor( model => model.StorAddress )
</th>
<th>
@Html.DisplayNameFor( model => model.City )
</th>
<th>
@Html.DisplayNameFor( model => model.State )
</th>
<th>
@Html.DisplayNameFor( model => model.Zip )
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor( modelItem => item.StorId )
</td>
<td>
@Html.DisplayFor( modelItem => item.StorName )
</td>
<td>
@Html.DisplayFor( modelItem => item.StorAddress )
</td>
<td>
@Html.DisplayFor( modelItem => item.City )
</td>
<td>
@Html.DisplayFor( modelItem => item.State )
</td>
<td>
@Html.DisplayFor( modelItem => item.Zip )
</td>
<td>
@Html.ActionLink( "Edit", "Edit", new {/* id = item.PrimaryKey */ } ) |
@Html.ActionLink( "Details", "Details", new { id = item.StorId } ) |
@Html.ActionLink( "Delete", "Delete", new { /* id = item.PrimaryKey */ } )
</td>
</tr>
}
</tbody>
</table>
選取Visual Studio 2019開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯,完成後可以同時執行兩個網站,只要在Visual Studio 2019開發工具,按「CTRL」+「F5」組合鍵來執行網站應用程式,請參考下圖所示:

圖 3:查詢一筆商店資料。
點選任一筆資料後方的「Details」超連結,便可看到資料呈現在畫面上,請參考下圖所示:

圖 4:顯示一筆商店資料。
新增資料
在ASP.NET Core MVC「MyWeb」專案中「HomeController」類別中加入兩個「Create」方法,第一個「Create」方法沒有參數;第二個「Create」方法接收一個「Store」型別的參數,方法上方標識「HttpPost」 Attribute。在標識「HttpPost」 Attribute的「Create」方法中,叫用「PublisherService」物件的「CreateStoreAsync」方法透過Web API新增資料,請參考以下程式列表:
- MyWeb\Controllers\HomeController.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using MyWeb.Models;
namespace MyWeb.Controllers {
public class HomeController : Controller {
private readonly ILogger<HomeController> _logger;
private readonly IHttpClientFactory _httpClientFactory;
public HomeController( ILogger<HomeController> logger, IHttpClientFactory httpClientFactory ) {
_logger = logger;
_httpClientFactory = httpClientFactory;
}
public async Task<IActionResult> Index( ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
ICollection<Store> stores = await client.GetStoresAsync( );
return View( stores );
}
public async Task<IActionResult> Details( string id ) {
if ( id == null ) {
return NotFound( );
}
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
Store store = await client.GetStoreByIdAsync( id );
if ( store == null ) {
return NotFound( );
}
return View( store );
}
public IActionResult Create( ) {
return View( );
}
[HttpPost]
public async Task<IActionResult> Create( Store store ) {
if ( ModelState.IsValid ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
await client.CreateStoreAsync( store );
return RedirectToAction( nameof( Index ) );
}
return View( store );
}
public IActionResult Privacy( ) {
return View( );
}
[ResponseCache( Duration = 0, Location = ResponseCacheLocation.None, NoStore = true )]
public IActionResult Error( ) {
return View( new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier } );
}
}
}
建立「Create」檢視
下一個步驟建立「Create」檢視,將游標停留在「HomeController」控制器程式設計畫面「Create」方法之中,按一下滑鼠右鍵,從快捷選單選取「Add View」。在「Add Razor View」對話盒中,設定:
- · 「View name」:「Create」。
- · 「Template」:「Create」。
- · 「Model class」:選取「Store」類別。
- · 勾選「Use a layout page」核取方塊。
然後按下「Add」按鈕。Visual Studio 2019便會在「Views\Home」資料夾下,新增一個「Create.cshtml」檔案,請參考下圖所示:

圖 5:建立「Create」檢視。
選取Visual Studio 開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。在Visual Studio開發工具,按CTRL+F5執行網站,點選「Index」檢視上的「Create New」連結,或在瀏覽器輸入以下網址(URL)進入新增資料畫面:
https://localhost:44303/Home/Create
輸入商店資料,然後按「Create」按鈕:

圖 6:輸入要新增的資料。
此時你會得到一個HTTP狀態碼201例外錯誤,請參考下圖所示:

圖 7:HTTP狀態碼201例外錯誤。
這是因為我們設計的ASP.NET Core Web API在資料新增後,會回傳HTTP 201狀態碼,與「NSwagStudio」工具產生的「PublisherService」類別程式碼預期的狀態碼「200」不一致的關係,你可以修改「PublisherService」類別「CreateStoreAsync」方法內「if (status_ == 200 )」這段程式碼為「if (status_ == 201 || status_ == 200)」。「CreateStoreAsync」方法目前的程式碼如下:
- MyWeb\PublisherService.cs
public async System.Threading.Tasks.Task<Store> CreateStoreAsync(Store body, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Stores/CreateStore");
var client_ = _httpClient;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
request_.Content = content_;
request_.Method = new System.Net.Http.HttpMethod("POST");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 201 || status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<Store>(response_, headers_).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (response_ != null)
response_.Dispose();
}
}
}
finally
{
}
}
選取Visual Studio 2019開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯,完成後可以同時執行兩個網站,只要在Visual Studio 2019開發工具,按「CTRL」+「F5」組合鍵來執行網站應用程式,試著新增一筆資料,若資料沒有問題,應該會成功的新增到資料庫,並跳轉到「Index」檢視顯示出新增的資料,請參考下圖所示:

圖 8:成功新增資料。
HTTP狀態碼201例外錯誤另一種解法
若不修改「PublisherService」的程式碼,要解決上述HTTP 201狀態碼例外錯誤的話,另一種解決方法是修改在ASP.NET Core MVC「MyWebAPI」專案中「StoresController」類別的「PostStore」方法,在成功將資料寫到資料庫之後,直接回傳「Task.FromResult( store )」這樣ASP.NET Core API在資料新增之後將會回傳HTTP 200狀態碼,請參考以下程式列表:
- MyWebAPI\Controllers\StoresController.cs
[HttpPost("CreateStore")]
public async Task<ActionResult<Store>> ( Store store ) {
_context.Stores.Add( store );
try {
await _context.SaveChangesAsync( );
}
catch ( DbUpdateException ) {
if ( StoreExists( store.StorId ) ) {
return Conflict( );
}
else {
throw;
}
}
return await Task.FromResult( store );
//return CreatedAtAction( "GetStore", new { id = store.StorId }, store );
}
修改資料
在ASP.NET Core MVC「MyWeb」專案中「HomeController」類別中加入兩個「Edit」方法程式碼,第一個「Edit」方法接收一個「string」型別的「id」參數;第二個「Edit」方法接收一個「string」型別的「id」參數與一個「Store」型別的「store」參數。在標識「HttpPost」 Attribute的「Edit」方法中,叫用「PublisherService」的「UpdateStoreAsync」方法將「Store」物件送到ASP.NET Core Web API做修改,然後利用「RedirectToAction」方法回到「Index」檢視,請參考以下程式列表:
- MyWeb\Controllers\HomeController.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using MyWeb.Models;
namespace MyWeb.Controllers {
public class HomeController : Controller {
private readonly ILogger<HomeController> _logger;
private readonly IHttpClientFactory _httpClientFactory;
public HomeController( ILogger<HomeController> logger, IHttpClientFactory httpClientFactory ) {
_logger = logger;
_httpClientFactory = httpClientFactory;
}
public async Task<IActionResult> Index( ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
ICollection<Store> stores = await client.GetStoresAsync( );
return View( stores );
}
public async Task<IActionResult> Details( string id ) {
if ( id == null ) {
return NotFound( );
}
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
Store store = await client.GetStoreByIdAsync( id );
if ( store == null ) {
return NotFound( );
}
return View( store );
}
public IActionResult Create( ) {
return View( );
}
[HttpPost]
public async Task<IActionResult> Create( Store store ) {
if ( ModelState.IsValid ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
await client.CreateStoreAsync( store );
return RedirectToAction( nameof( Index ) );
}
return View( store );
}
public async Task<IActionResult> Edit(string id ) {
if ( id == null ) {
return NotFound( );
}
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
Store store = await client.GetStoreByIdAsync( id );
if ( store == null ) {
return NotFound( );
}
return View( store );
}
[HttpPost]
public async Task<IActionResult> Edit( string id, Store store ) {
if ( id != store.StorId) {
return NotFound( );
}
if ( ModelState.IsValid ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
await client.UpdateStoreAsync(id, store );
return RedirectToAction( nameof( Index ) );
}
return View( store );
}
public IActionResult Privacy( ) {
return View( );
}
[ResponseCache( Duration = 0, Location = ResponseCacheLocation.None, NoStore = true )]
public IActionResult Error( ) {
return View( new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier } );
}
}
}
建立「Edit」檢視
將游標停留在「HomeController」控制器程式設計畫面「Edit」方法之中,按一下滑鼠右鍵,從快捷選單選取「Add View」。在「Add Razor View」對話盒中,設定以下項目:
- · 「View name」:「Edit」。
- · 「Template」:「Edit」。
- · 「Model class」:選取「Store」類別。
- · 勾選「Use a layout page」核取方塊。
然後按下「Add」按鈕。Visual Studio 2019便會在「Views\Home」資料夾下,新增一個「Edit.cshtml」檔案,請參考下圖所示:

圖 9:建立「Edit」檢視。
目前「Edit」檢視的程式碼看起來如下:
- MyWeb\Views\Home\Edit.cshtml
@model MyWeb.Store
@{
ViewData["Title"] = "Edit";
}
<h1> Edit </h1>
<h4> Store </h4>
<hr />
<div class = "row">
<div class = "col-md-4">
<form asp-action = "Edit">
<div asp-validation-summary = "ModelOnly" class = "text-danger"> </div>
<div class = "form-group">
<label asp-for = "StorId" class = "control-label"> </label>
<input asp-for = "StorId" class = "form-control" />
<span asp-validation-for = "StorId" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "StorName" class = "control-label"> </label>
<input asp-for = "StorName" class = "form-control" />
<span asp-validation-for = "StorName" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "StorAddress" class = "control-label"> </label>
<input asp-for = "StorAddress" class = "form-control" />
<span asp-validation-for = "StorAddress" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "City" class = "control-label"> </label>
<input asp-for = "City" class = "form-control" />
<span asp-validation-for = "City" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "State" class = "control-label"> </label>
<input asp-for = "State" class = "form-control" />
<span asp-validation-for = "State" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "Zip" class = "control-label"> </label>
<input asp-for = "Zip" class = "form-control" />
<span asp-validation-for = "Zip" 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>
修改「Index」檢視,同樣在叫用「Html.ActionLink」方法產生「Edit」超連結時,第三個參數傳入一個匿名物件,指定要編輯的商店編號,以方便切換到「Edit」資料畫面:
Html.ActionLink( "Edit ", " Edit ", new { id = item.StorId } )
目前「Index」檢視的程式碼看起來如下:
- MyWeb\Views\Home\Index.cshtml
@model IEnumerable<MyWeb.Store>
@{
ViewData["Title"] = "Index";
}
<h1> Index </h1>
<p>
<a asp-action = "Create"> Create New </a>
</p>
<table class = "table">
<thead>
<tr>
<th>
@Html.DisplayNameFor( model => model.StorId )
</th>
<th>
@Html.DisplayNameFor( model => model.StorName )
</th>
<th>
@Html.DisplayNameFor( model => model.StorAddress )
</th>
<th>
@Html.DisplayNameFor( model => model.City )
</th>
<th>
@Html.DisplayNameFor( model => model.State )
</th>
<th>
@Html.DisplayNameFor( model => model.Zip )
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor( modelItem => item.StorId )
</td>
<td>
@Html.DisplayFor( modelItem => item.StorName )
</td>
<td>
@Html.DisplayFor( modelItem => item.StorAddress )
</td>
<td>
@Html.DisplayFor( modelItem => item.City )
</td>
<td>
@Html.DisplayFor( modelItem => item.State )
</td>
<td>
@Html.DisplayFor( modelItem => item.Zip )
</td>
<td>
@Html.ActionLink( "Edit", "Edit", new { id = item.StorId }) |
@Html.ActionLink( "Details", "Details", new { id = item.StorId }) |
@Html.ActionLink( "Delete", "Delete", new { /* id = item.PrimaryKey */ })
</td>
</tr>
}
</tbody>
</table>
選取Visual Studio 2019開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯,完成後可以同時執行兩個網站,只要在Visual Studio 2019開發工具,按「CTRL」+「F5」組合鍵來執行網站應用程式,點選任一筆資料後方的「Edit」連結,便可看到資料呈現在編輯畫面上,請參考下圖所示:

圖 10:儲存編輯的資料。
和新增資料的結果類似,這次資料儲存時會發生HTTP狀態碼204號錯誤,執行結果請參考下圖所示:

圖 11: HTTP狀態碼204例外錯誤。
修改「PublisherService」類別「UpdateStoreAsync」方法內「if (status_ == 200 )」這段程式碼為「if (status_ == 200 || status_ == 204)」。「UpdateStoreAsync」方法目前的程式碼如下:
- MyWeb\PublisherService.cs
public async System.Threading.Tasks.Task UpdateStoreAsync(string id, Store body, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/Stores/UpdateStore/{id}");
urlBuilder_.Replace("{id}", System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture)));
var client_ = _httpClient;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
request_.Content = content_;
request_.Method = new System.Net.Http.HttpMethod("PUT");
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200 || status_ == 204 )
{
return;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (response_ != null)
response_.Dispose();
}
}
}
finally
{
}
}
選取Visual Studio 2019開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯,完成後可以同時執行兩個網站,只要在Visual Studio 2019開發工具,按「CTRL」+「F5」組合鍵來執行網站應用程式,試著修改一筆資料,若資料沒有問題,這次應該會成功的修改資料,並跳轉到「Index」檢視顯示出修改過的資料。
刪除資料
在ASP.NET Core MVC「MyWeb」專案中「HomeController」類別中加入「Delete」方法與「DeleteConfirm」方法。由於這兩個方法的參數都是一個「string」型別的「id」變數,因此方法名稱不能命名為相同,不符合程式語言方法多載(Method Overload)的原則。解決方式是將兩個方法分別取不同名稱:「Delete」與「DeleteConfirm」,並在「DeleteConfirm」方法上方套用「ActionName("Delete")」Attribute表示這是資料使用HTTP POST提交時,要執行的「Delete」方法。
在標識「HttpPost」 Attribute的「Delete」方法中,叫用「PublisherService」的「DeleteStoreAsync」方法讓Web API刪除資料庫的資料,然後利用「RedirectToAction」方法回到「Index」檢視,請參考以下程式列表:
- MyWebAPI\Controllers\StoresController.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using MyWeb.Models;
namespace MyWeb.Controllers {
public class HomeController : Controller {
private readonly ILogger<HomeController> _logger;
private readonly IHttpClientFactory _httpClientFactory;
public HomeController( ILogger<HomeController> logger, IHttpClientFactory httpClientFactory ) {
_logger = logger;
_httpClientFactory = httpClientFactory;
}
public async Task<IActionResult> Index( ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
ICollection<Store> stores = await client.GetStoresAsync( );
return View( stores );
}
public async Task<IActionResult> Details( string id ) {
if ( id == null ) {
return NotFound( );
}
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
Store store = await client.GetStoreByIdAsync( id );
if ( store == null ) {
return NotFound( );
}
return View( store );
}
public IActionResult Create( ) {
return View( );
}
[HttpPost]
public async Task<IActionResult> Create( Store store ) {
if ( ModelState.IsValid ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
await client.CreateStoreAsync( store );
return RedirectToAction( nameof( Index ) );
}
return View( store );
}
public async Task<IActionResult> Edit( string id ) {
if ( id == null ) {
return NotFound( );
}
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
Store store = await client.GetStoreByIdAsync( id );
if ( store == null ) {
return NotFound( );
}
return View( store );
}
[HttpPost]
public async Task<IActionResult> Edit( string id, Store store ) {
if ( id != store.StorId ) {
return NotFound( );
}
if ( ModelState.IsValid ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
await client.UpdateStoreAsync( id, store );
return RedirectToAction( nameof( Index ) );
}
return View( store );
}
public async Task<IActionResult> Delete( string id ) {
if ( id == null ) {
return NotFound( );
}
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
Store store = await client.GetStoreByIdAsync( id );
if ( store == null ) {
return NotFound( );
}
return View( store );
}
[HttpPost, ActionName( "Delete" )]
public async Task<IActionResult> DeleteConfirmed( string id ) {
PublisherService client =
new PublisherService( "https://localhost:44311/", _httpClientFactory.CreateClient( ) );
await client.DeleteStoreAsync( id );
return RedirectToAction( nameof( Index ) );
}
public IActionResult Privacy( ) {
return View( );
}
[ResponseCache( Duration = 0, Location = ResponseCacheLocation.None, NoStore = true )]
public IActionResult Error( ) {
return View( new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier } );
}
}
}
建立「Delete」檢視
將游標停留在「HomeController」控制器程式設計畫面「Delete」方法之中,按一下滑鼠右鍵,從快捷選單選取「Add View」。在「Add Razor View」對話盒中,設定以下項目:
- · 「View name」:「Delete」。
- · 「Template」:「Delete」。
- · 「Model class」:選取「Store」類別。
- · 勾選「Use a layout page」核取方塊。
然後按下「Add」按鈕。Visual Studio 2019便會在「Views\Home」資料夾下,新增一個「Delete.cshtml」檔案,請參考下圖所示:

圖 12:建立「Delete」檢視。
然後按下「Add」按鈕。Visual Studio 2019便會在「Views\Home」資料夾下,新增一個「Delete.cshtml」檔案。目前「Delete」檢視的程式碼看起來如下:
- MyWeb\Views\Home\Delete.cshtml
@model MyWeb.Store
@{
ViewData["Title"] = "Delete";
}
<h1> Delete </h1>
<h3> Are you sure you want to delete this? </h3>
<div>
<h4> Store </h4>
<hr />
<dl class = "row">
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.StorId )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.StorId )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.StorName )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.StorName )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.StorAddress )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.StorAddress )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.City )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.City )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.State )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.State )
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor( model => model.Zip )
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor( model => model.Zip )
</dd>
</dl>
<form asp-action = "Delete">
<input type = "submit" value = "Delete" class = "btn btn-danger" /> |
<a asp-action = "Index"> Back to List </a>
</form>
</div>
修改「Index」檢視,同樣在叫用「Html.ActionLink」方法產生「Delete」超連結時,第三個參數傳入一個匿名物件,指定要刪除的商店編號,以方便切換到「Delete」資料確認畫面:
Html.ActionLink( "Delete ", " Delete ", new { id = item.StorId } )
目前「Index」檢視的程式碼看起來如下:
- MyWeb\Views\Home\Index.cshtml
@model IEnumerable<MyWeb.Store>
@{
ViewData["Title"] = "Index";
}
<h1> Index </h1>
<p>
<a asp-action = "Create"> Create New </a>
</p>
<table class = "table">
<thead>
<tr>
<th>
@Html.DisplayNameFor( model => model.StorId )
</th>
<th>
@Html.DisplayNameFor( model => model.StorName )
</th>
<th>
@Html.DisplayNameFor( model => model.StorAddress )
</th>
<th>
@Html.DisplayNameFor( model => model.City )
</th>
<th>
@Html.DisplayNameFor( model => model.State )
</th>
<th>
@Html.DisplayNameFor( model => model.Zip )
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor( modelItem => item.StorId )
</td>
<td>
@Html.DisplayFor( modelItem => item.StorName )
</td>
<td>
@Html.DisplayFor( modelItem => item.StorAddress )
</td>
<td>
@Html.DisplayFor( modelItem => item.City )
</td>
<td>
@Html.DisplayFor( modelItem => item.State )
</td>
<td>
@Html.DisplayFor( modelItem => item.Zip )
</td>
<td>
@Html.ActionLink( "Edit", "Edit", new { id=item.StorId }) |
@Html.ActionLink( "Details", "Details", new { id=item.StorId }) |
@Html.ActionLink( "Delete", "Delete", new { id = item.StorId } )
</td>
</tr>
}
</tbody>
</table>
選取Visual Studio 2019開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯,完成後可以同時執行兩個網站,只要在Visual Studio 2019開發工具,按「CTRL」+「F5」組合鍵來執行網站應用程式,點選任一筆資料後方的「Delete」連結,便可看到資料呈現在刪除確認畫面上,點選「Delete」按鈕便會將資料刪除,請參考下圖所示:

圖 13:確認刪除。