在 ASP.NET Web API 1 中啟用 CRUD 作業
作者:Mike Wasson
本教學課程說明如何使用 ASP.NET 4.x ASP.NET Web API,在 HTTP 服務中支援 CRUD 作業。
教學課程中使用的軟體版本
- Visual Studio 2012
- Web API 1 (也適用于 Web API 2)
CRUD 代表「建立、讀取、更新和刪除」,這是四個基本資料庫作業。 許多 HTTP 服務也會透過 REST 或類似 REST 的 API 來建立 CRUD 作業的模型。
在本教學課程中,您將建置非常簡單的 Web API 來管理產品清單。 每個產品都會包含名稱、價格和類別 (,例如「玩具」或「硬體」) ,加上產品識別碼。
產品 API 將會公開下列方法。
動作 | HTTP method | 相對 URI |
---|---|---|
取得所有產品的清單 | GET | /api/products |
依照識別碼取得產品 | GET | /api/products/id |
依類別取得產品 | GET | /api/products?category=category |
建立新的產品 | POST | /api/products |
更新產品 | PUT | /api/products/id |
刪除產品 | 刪除 | /api/products/id |
請注意,某些 URI 包含路徑中的產品識別碼。 例如,若要取得識別碼為 28 的產品,用戶端會傳送 的 http://hostname/api/products/28
GET 要求。
資源
產品 API 會定義兩種資源類型的 URI:
資源 | URI |
---|---|
所有產品的清單。 | /api/products |
個別產品。 | /api/products/id |
方法
(GET、PUT、POST 和 DELETE) 四個主要 HTTP 方法可以對應至 CRUD 作業,如下所示:
- GET 會擷取位於指定 URI 的資源表示。 GET 應該不會對伺服器造成副作用。
- PUT 會在指定的 URI 更新資源。 如果伺服器允許用戶端指定新的 URI,PUT 也可以用來在指定的 URI 上建立新的資源。 在本教學課程中,API 不支援透過 PUT 建立。
- POST 會建立新的資源。 伺服器會指派新物件的 URI,並傳回此 URI 做為回應訊息的一部分。
- DELETE 會在指定的 URI 刪除資源。
注意:PUT 方法會取代整個產品實體。 也就是說,用戶端預期會傳送更新產品的完整標記法。 如果您想要支援部分更新,建議使用 PATCH 方法。 本教學課程不會實作 PATCH。
建立新的 Web API 專案
從執行 Visual Studio 開始,然後從 [開始] 頁面選取 [新增專案]。 或者,從 [ 檔案] 功能表中,選取 [ 新增 ],然後選取 [ 專案]。
在 [ 範本] 窗格中,選取 [ 已安裝的範本 ],然後展開 [Visual C# ] 節點。 在 [Visual C#] 底下,選取 [Web]。 在專案範本清單中,選取 [ASP.NET MVC 4 Web 應用程式]。 將專案命名為 「ProductStore」,然後按一下 [ 確定]。
在 [ 新增 ASP.NET MVC 4 專案 ] 對話方塊中,選取 [Web API ],然後按一下 [ 確定]。
新增模型
「模型」 是代表您應用程式中資料的物件。 在 ASP.NET Web API中,您可以使用強型別 CLR 物件作為模型,而且它們會自動序列化為用戶端的 XML 或 JSON。
針對 ProductStore API,我們的資料是由產品所組成,因此我們將建立名為 Product
的新類別。
如果沒有顯示 [方案總管],請按一下 [檢視] 功能表,然後選取 [方案總管]。 在 [方案總管] 中,以滑鼠右鍵按一下Models資料夾。 從操作功能表中,選取 [ 新增],然後選取 [ 類別]。 將類別命名為 「Product」。
將下列屬性新增至 Product
類別。
namespace ProductStore.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
}
}
加入儲存機制
我們需要儲存產品集合。 最好將集合與我們的服務實作分開。 如此一來,我們可以變更備份存放區,而不需重寫服務類別。 這種類型的設計稱為存放 庫 模式。 首先,定義存放庫的泛型介面。
在 [方案總管] 中,以滑鼠右鍵按一下Models資料夾。 選取 [新增],然後選取 [新增專案]。
在 [ 範本] 窗格中,選取 [ 已安裝的範本 ],然後展開 [C#] 節點。 在 [C#] 底下,選取 [ 程式碼]。 在程式碼範本清單中,選取 [ 介面]。 將介面命名為 「IProductRepository」。
新增下列實作:
namespace ProductStore.Models
{
public interface IProductRepository
{
IEnumerable<Product> GetAll();
Product Get(int id);
Product Add(Product item);
void Remove(int id);
bool Update(Product item);
}
}
現在,將另一個類別新增至 Models 資料夾,名為 「ProductRepository」。這個類別會實作 IProductRepository
介面。 新增下列實作:
namespace ProductStore.Models
{
public class ProductRepository : IProductRepository
{
private List<Product> products = new List<Product>();
private int _nextId = 1;
public ProductRepository()
{
Add(new Product { Name = "Tomato soup", Category = "Groceries", Price = 1.39M });
Add(new Product { Name = "Yo-yo", Category = "Toys", Price = 3.75M });
Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M });
}
public IEnumerable<Product> GetAll()
{
return products;
}
public Product Get(int id)
{
return products.Find(p => p.Id == id);
}
public Product Add(Product item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
item.Id = _nextId++;
products.Add(item);
return item;
}
public void Remove(int id)
{
products.RemoveAll(p => p.Id == id);
}
public bool Update(Product item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
int index = products.FindIndex(p => p.Id == item.Id);
if (index == -1)
{
return false;
}
products.RemoveAt(index);
products.Add(item);
return true;
}
}
}
存放庫會將清單保留在本機記憶體中。 這適用于教學課程,但在實際應用程式中,您會在外部儲存資料庫或雲端儲存體中的資料。 存放庫模式可讓您更輕鬆地稍後變更實作。
新增 Web API 控制器
如果您已使用 ASP.NET MVC,則您已經熟悉控制器。 在 ASP.NET Web API 中,控制器是處理來自用戶端 HTTP 要求的類別。 [新增專案] 精靈會在建立專案時為您建立兩個控制器。 若要查看它們,請展開 [控制器] 資料夾中的 [方案總管]。
- HomeController 是傳統的 ASP.NET MVC 控制器。 它負責為網站提供 HTML 頁面,而且與 Web API 不直接相關。
- ValuesController 是 WebAPI 控制器範例。
請繼續並刪除 ValuesController,方法是以滑鼠右鍵按一下 方案總管 中的檔案,然後選取 [刪除]。現在新增控制器,如下所示:
在 [方案總管] 中,於 Controllers 資料夾上按一下滑鼠右鍵。 選取 [新增],然後選取 [控制器]。
在 [ 新增控制器 精靈] 中,將控制器命名為 「ProductsController」。 在 [ 範本 ] 下拉式清單中,選取 [空白 API 控制器]。 然後按一下 [ 新增]。
注意
您不需要將控制器放入名為 Controllers 的資料夾。 資料夾名稱不重要;這是組織原始程式檔的便利方式。
[ 新增控制器 精靈] 會在 Controllers 資料夾中建立名為 ProductsController.cs 的檔案。 如果此檔案尚未開啟,請按兩下檔案將它開啟。 新增下列 using 語句:
using ProductStore.Models;
新增保留 IProductRepository 實例的 欄位。
public class ProductsController : ApiController
{
static readonly IProductRepository repository = new ProductRepository();
}
注意
在控制器中呼叫 new ProductRepository()
不是最佳設計,因為它會將控制器系結至 的特定實作 IProductRepository
。 如需更好的方法,請參閱 使用 Web API 相依性解析程式。
取得資源
ProductStore API 會將數個「讀取」動作公開為 HTTP GET 方法。 每個動作都會對應至 類別中的 ProductsController
方法。
動作 | HTTP method | 相對 URI |
---|---|---|
取得所有產品的清單 | GET | /api/products |
依照識別碼取得產品 | GET | /api/products/id |
依類別取得產品 | GET | /api/products?category=category |
若要取得所有產品的清單,請將這個方法新增至 ProductsController
類別:
public class ProductsController : ApiController
{
public IEnumerable<Product> GetAllProducts()
{
return repository.GetAll();
}
// ....
}
方法名稱以 「Get」 開頭,因此依慣例它會對應至 GET 要求。 此外,因為 方法沒有參數,所以它會對應至路徑中不包含 「id」 區段的 URI。
若要依識別碼取得產品,請將這個方法新增至 ProductsController
類別:
public Product GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return item;
}
這個方法名稱也會以 「Get」 開頭,但方法有名為 id的參數。此參數會對應至 URI 路徑的 「id」 區段。 ASP.NET Web API架構會自動將識別碼轉換成參數的正確資料類型 (int) 。
如果id無效,GetProduct 方法會擲回HttpResponseException類型的例外狀況。 此例外狀況將由架構轉譯為 404 (找不到) 錯誤。
最後,新增方法以依類別尋找產品:
public IEnumerable<Product> GetProductsByCategory(string category)
{
return repository.GetAll().Where(
p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
}
如果要求 URI 有查詢字串,Web API 會嘗試比對查詢參數與控制器方法上的參數。 因此,「api/products?category=category」 格式的 URI 會對應至此方法。
建立資源
接下來,我們會將 方法新增至 類別, ProductsController
以建立新產品。 以下是方法的簡單實作:
// Not the final implementation!
public Product PostProduct(Product item)
{
item = repository.Add(item);
return item;
}
請注意這個方法的兩件事:
- 方法名稱開頭為 「Post...」。 若要建立新產品,用戶端會傳送 HTTP POST 要求。
- 方法會採用 Product 類型的參數。 在 Web API 中,具有複雜類型的參數會從要求主體還原序列化。 因此,我們預期用戶端會以 XML 或 JSON 格式傳送產品物件的序列化標記法。
此實作將會正常運作,但尚未完全完成。 在理想情況下,我們希望 HTTP 回應包含下列專案:
- 回應碼: 根據預設,Web API 架構會將回應狀態碼設定為 200 (OK) 。 但根據 HTTP/1.1 通訊協定,當 POST 要求產生資源建立時,伺服器應該回復狀態為 201 (建立) 。
- 位置: 當伺服器建立資源時,它應該會在回應的 Location 標頭中包含新資源的 URI。
ASP.NET Web API可讓您輕鬆地操作 HTTP 回應訊息。 以下是改善的實作:
public HttpResponseMessage PostProduct(Product item)
{
item = repository.Add(item);
var response = Request.CreateResponse<Product>(HttpStatusCode.Created, item);
string uri = Url.Link("DefaultApi", new { id = item.Id });
response.Headers.Location = new Uri(uri);
return response;
}
請注意,方法傳回類型現在是 HttpResponseMessage。 藉由傳回 HttpResponseMessage 而非 Product,我們可以控制 HTTP 回應訊息的詳細資料,包括狀態碼和位置標頭。
CreateResponse方法會建立HttpResponseMessage,並自動將 Product 物件的序列化標記法寫入回應訊息的本文中。
注意
此範例不會驗證 Product
。 如需模型驗證的相關資訊,請參閱ASP.NET Web API 中的模型驗證。
更新資源
使用 PUT 更新產品很簡單:
public void PutProduct(int id, Product product)
{
product.Id = id;
if (!repository.Update(product))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
方法名稱開頭為 「Put...」,因此 Web API 會比對 PUT 要求。 方法會採用兩個參數:產品識別碼和更新的產品。 id參數取自 URI 路徑,且產品參數會從要求本文還原序列化。 根據預設,ASP.NET Web API架構會從要求本文取得路由和複雜類型的簡單參數類型。
刪除資源
若要刪除資源,請定義「刪除...」。方法。
public void DeleteProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
repository.Remove(id);
}
如果 DELETE 要求成功,它可以使用描述狀態的實體主體傳回狀態 200 (OK) ;狀態 202 (如果刪除仍在擱置中,則為 [已接受]) ;或狀態 204 (沒有實體主體的無內容) 。 在此情況下, DeleteProduct
此方法具有 void
傳回類型,因此 ASP.NET Web API會自動將此轉譯為狀態碼 204 ([沒有內容]) 。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應