ASP.NET Core: Kích hoạt các hoạt động CRUD trong ASP.NET Web API
Hướng dẫn này cho thấy cách hỗ trợ các hoạt động CRUD trong dịch vụ HTTP bằng API Web ASP.NET cho ASP.NET 4.x.
Các phiên bản phần mềm được sử dụng trong hướng dẫn
- Visual Studio 2012
- API Web 1 (cũng hoạt động với API Web 2)
CRUD là viết tắt của "Create, Read, Update và Delete", là bốn thao tác cơ sở dữ liệu cơ bản. Nhiều dịch vụ HTTP cũng mô hình hóa các hoạt động CRUD thông qua các API tương tự REST hoặc API REST.
Trong hướng dẫn này, bạn sẽ xây dựng một API web rất đơn giản để quản lý danh sách sản phẩm (product). Mỗi sản phẩm sẽ chứa tên (name), giá (price) và danh mục (category) (chẳng hạn như "toys" hoặc "hardware"), cùng với ID sản phẩm.
API sản phẩm sẽ hiển thị các phương thức sau.
Action | Phương thức HTTP | URI tương đối |
---|---|---|
Nhận danh sách tất cả các sản phẩm | GET | /api/products |
Nhận sản phẩm theo ID | GET | /api/products/id |
Nhận sản phẩm theo danh mục | GET | /api/products?category=category |
Tạo một sản phẩm mới | POST | /api/products |
Cập nhật một sản phẩm | PUT | /api/products/id |
Xóa một sản phẩm | DELETE | /api/products/id |
Lưu ý rằng một số URI bao gồm cả ID sản phẩm trong đường dẫn. Ví dụ: để nhận sản phẩm có ID là 28, khách hàng sẽ gửi yêu cầu GET cho http://hostname/api/products/28
.
Tài nguyên
API sản phẩm định nghĩa các URI cho hai loại tài nguyên:
Resource | URI |
---|---|
Danh sách tất cả các sản phẩm. | /api/products |
Một sản phẩm riêng lẻ. | /api/products/id |
Các phương thức
Bốn phương thức HTTP chính (GET, PUT, POST và DELETE) có thể được ánh xạ tới các hoạt động CRUD như sau:
- GET truy xuất biểu diễn của tài nguyên tại một URI được chỉ định. GET sẽ không có tác dụng phụ trên máy chủ.
- PUT cập nhật tài nguyên tại một URI được chỉ định. PUT cũng có thể được sử dụng để tạo tài nguyên mới tại một URI được chỉ định, nếu máy chủ cho phép khách hàng chỉ định các URI mới. Đối với hướng dẫn này, API sẽ không hỗ trợ tạo (creation) thông qua PUT.
- POST tạo một tài nguyên mới. Máy chủ gán URI cho đối tượng mới và trả về URI này như một phần của thông báo phản hồi.
- DELETE xóa tài nguyên tại một URI được chỉ định.
Lưu ý: Phương thức PUT thay thế toàn bộ thực thể product. Nghĩa là, khách hàng phải gửi bản trình bày đầy đủ về sản phẩm được cập nhật. Nếu bạn muốn hỗ trợ cập nhật một phần, phương thức PATCH được ưu tiên. Hướng dẫn này không triển khai PATCH.
Tạo một dự án API Web mới
Bắt đầu bằng cách chạy Visual Studio và chọn New Project từ trang Start. Hoặc từ menu File, chọn New rồi chọn Project.
Trong ngăn Templates, chọn Installed Templates và mở rộng nút Visual C#. Trong Visual C#, chọn Web. Trong danh sách mẫu dự án, chọn ASP.NET MVC 4 Web Application. Đặt tên cho dự án là "ProductStore" và nhấp vào OK.
Trong hộp thoại New ASP.NET MVC 4 Project, chọn Web API và nhấp vào OK.
Thêm model
Model là một đối tượng đại diện cho dữ liệu trong ứng dụng của bạn. Trong ASP.NET Web API, bạn có thể sử dụng các đối tượng CLR được định kiểu mạnh làm mô hình và chúng sẽ tự động được tuần tự hóa thành XML hoặc JSON cho máy khách.
Đối với API ProductStore, dữ liệu của ta bao gồm các sản phẩm, vì vậy ta sẽ tạo một lớp mới có tên Product
.
Nếu Solution Explorer chưa hiển thị, hãy nhấp vào menu View và chọn Solution Explorer. Trong Solution Explorer, bấm chuột phải vào thư mục Models. Từ menu ngữ cảnh, chọn Add, sau đó chọn Class. Đặt tên cho class là "Product".
Thêm các property sau vào lớp 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; }
}
}
Thêm một Repository
Chúng ta cần lưu trữ một collection các sản phẩm. Bạn nên tách collection khỏi việc triển khai dịch vụ. Bằng cách đó, ta có thể thay đổi kho dự phòng mà không cần viết lại lớp dịch vụ. Kiểu thiết kế này được gọi là mẫu repository. Bắt đầu bằng cách định nghĩa generic interface cho repository.
Trong Solution Explorer, bấm chuột phải vào thư mục Models. Chọn Add, sau đó chọn New Item.
Trong ngăn Templates, chọn Installed Templates và mở rộng nút C#. Trong C#, chọn Code. Trong danh sách code template, chọn Interface. Đặt tên cho interface là "INProductRepository".
Thêm cách thực hiện sau:
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);
}
}
Bây giờ hãy thêm một lớp khác vào thư mục Models, có tên là "ProductRepository". Lớp này sẽ thực thi interface IProductRepository
. Thêm cách thực hiện sau:
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;
}
}
}
Repository sẽ lưu giữ list trong bộ nhớ cục bộ. Điều này là ổn đối với hướng dẫn này, nhưng trong ứng dụng thực, bạn sẽ lưu trữ dữ liệu bên ngoài, cơ sở dữ liệu hoặc trong bộ lưu trữ đám mây. Mẫu repository sẽ giúp việc thay đổi triển khai sau này dễ dàng hơn.
Thêm Controller API Web
Nếu bạn đã làm việc với ASP.NET MVC thì bạn đã quen với controller. Trong ASP.NET Web API, controller là lớp xử lý các yêu cầu HTTP từ máy khách. Trình hướng dẫn New Project đã tạo hai controller cho bạn khi tạo dự án. Để xem chúng, hãy mở rộng thư mục Controllers trong Solution Explorer.
- HomeController là controller ASP.NET MVC truyền thống. Nó chịu trách nhiệm phục vụ các trang HTML cho trang web và không liên quan trực tiếp đến API web của ta.
- ValuesController là một controller WebAPI mẫu.
Hãy tiếp tục và xóa ValuesController bằng cách nhấp chuột phải vào tệp trong Solution Explorer và chọn Delete. Bây giờ thêm một controller mới, như sau:
Trong Solution Explorer, bấm chuột phải vào thư mục Controllers. Chọn Add rồi chọn Controller.
Trong trình hướng dẫn Add Controller, hãy đặt tên cho controller là "ProductsController". Trong danh sách thả xuống Template, chọn Empty API Controller. Sau đó nhấp vào Add.
Ghi chú
Không cần thiết phải đặt controller của bạn vào thư mục có tên Controllers. Tên thư mục không quan trọng; nó chỉ đơn giản là một cách thuận tiện để sắp xếp các tập tin nguồn của bạn.
Trình hướng dẫn Add Controller sẽ tạo một tệp có tên ProductsController.cs trong thư mục Controllers. Nếu tệp này chưa được mở, hãy bấm đúp vào tệp để mở. Thêm câu lệnh using sau:
using ProductStore.Models;
Thêm trường chứa thể hiện IProductRepository.
public class ProductsController : ApiController
{
static readonly IProductRepository repository = new ProductRepository();
}
Ghi chú
Gọi new ProductRepository()
trong controller không phải là thiết kế tốt nhất vì nó liên kết controller với việc triển khai cụ thể của IProductRepository
. Để có cách tiếp cận tốt hơn, hãy xem Sử dụng Trình giải quyết phụ thuộc API Web.
Nhận tài nguyên
API ProductStore sẽ hiển thị một số hành động "read" dưới dạng phương thức HTTP GET. Mỗi hành động sẽ tương ứng với một phương thức trong lớp ProductsController
.
Action | Phương thức HTTP | URI tương đối |
---|---|---|
Nhận danh sách tất cả các sản phẩm | GET | /api/products |
Nhận sản phẩm theo ID | GET | /api/products/id |
Nhận sản phẩm theo danh mục | GET | /api/products?category=category |
Để có được danh sách tất cả các sản phẩm, hãy thêm phương thức sau vào lớp ProductsController
:
public class ProductsController : ApiController
{
public IEnumerable<Product> GetAllProducts()
{
return repository.GetAll();
}
// ....
}
Tên phương thức bắt đầu bằng "Get", do đó, theo quy ước, nó ánh xạ tới các yêu cầu GET. Ngoài ra, còn một lý do nữa vì phương thức này không có tham số nên nó ánh xạ tới một URI không chứa phân đoạn "id" trong đường dẫn.
Để lấy sản phẩm theo ID, hãy thêm phương thức này vào lớp ProductsController
:
public Product GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return item;
}
Tên phương thức này cũng bắt đầu bằng "Get", nhưng phương thức này có tham số có tên id. Tham số này được ánh xạ tới phân đoạn "id" của đường dẫn URI. Framework API Web ASP.NET tự động chuyển đổi ID thành kiểu dữ liệu chính xác (int) cho tham số.
Phương thức GetProduct đưa ra một ngoại lệ thuộc loại HttpResponseException nếu id không hợp lệ. Ngoại lệ này sẽ được framework dịch thành lỗi 404 (Không tìm thấy).
Cuối cùng, thêm phương thức tìm sản phẩm theo danh mục:
public IEnumerable<Product> GetProductsByCategory(string category)
{
return repository.GetAll().Where(
p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
}
Nếu URI yêu cầu có chuỗi truy vấn, API Web sẽ cố gắng khớp các tham số truy vấn với các tham số trên phương thức controller. Do đó, URI có dạng "api/products?category=category" sẽ ánh xạ tới phương thức này.
Tạo tài nguyên
Tiếp theo, chúng ta sẽ thêm một phương thức vào lớp ProductsController
để tạo một sản phẩm mới. Đây là cách thực hiện đơn giản của phương thức này:
// Not the final implementation!
public Product PostProduct(Product item)
{
item = repository.Add(item);
return item;
}
Lưu ý hai điều về phương pháp này:
- Tên phương thức bắt đầu bằng "Post ...". Để tạo một sản phẩm mới, thì client sẽ gửi yêu cầu HTTP POST.
- Phương thức này lấy một tham số thuộc loại Product. Trong API Web, các tham số có kiểu phức tạp sẽ được giải tuần tự hóa khỏi phần thân yêu cầu (request body). Do đó, ta mong đợi client gửi bản trình bày được tuần tự hóa của một đối tượng sản phẩm ở định dạng XML hoặc JSON.
Việc triển khai này sẽ hoạt động nhưng nó chưa hoàn chỉnh. Lý tưởng nhất là ta muốn phản hồi HTTP bao gồm những nội dung sau:
- Mã phản hồi: Theo mặc định, framework API Web đặt mã trạng thái phản hồi thành 200 (OK). Nhưng theo giao thức HTTP/1.1, khi yêu cầu POST dẫn đến việc tạo tài nguyên, máy chủ sẽ phản hồi với trạng thái 201 (Created).
- Vị trí: Khi máy chủ tạo tài nguyên, nó phải bao gồm URI của tài nguyên mới trong header Location của phản hồi.
ASP.NET Web API giúp bạn dễ dàng thao tác với thông báo phản hồi HTTP. Đây là cách thực hiện được cải thiện:
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;
}
Lưu ý rằng kiểu trả về của phương thức hiện là HttpResponseMessage. Bằng cách trả về HttpResponseMessage thay vì Product, ta có thể kiểm soát chi tiết của thông báo phản hồi HTTP, bao gồm mã trạng thái và header Location.
Phương thức CreateResponse tạo một HttpResponseMessage và tự động ghi một biểu diễn được tuần tự hóa của đối tượng Product vào phần nội dung của thông báo phản hồi.
Ghi chú
Ví dụ này không xác thực file Product
. Để biết thông tin về model validation, hãy xem Model validation trong API Web ASP.NET.
Cập nhật tài nguyên
Cập nhật sản phẩm bằng PUT rất đơn giản:
public void PutProduct(int id, Product product)
{
product.Id = id;
if (!repository.Update(product))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
Tên phương thức bắt đầu bằng "Put...", do đó API Web khớp nó với các yêu cầu PUT. Phương thức này có hai tham số, ID sản phẩm và sản phẩm được cập nhật. Tham số id được lấy từ đường dẫn URI và tham số product được giải tuần tự hóa khỏi nội dung yêu cầu. Theo mặc định, framework API Web ASP.NET lấy các loại tham số đơn giản từ route và các kiểu phức tạp từ phần thân yêu cầu.
Xóa tài nguyên
Để xóa tài nguyên, ta định nghĩa phương thức "Delete...".
public void DeleteProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
repository.Remove(id);
}
Nếu yêu cầu DELETE thành công, nó có thể trả về trạng thái 200 (OK) với phần nội dung thực thể mô tả trạng thái; trạng thái 202 (Accepted) nếu việc xóa vẫn đang chờ xử lý; hoặc trạng thái 204 (No Content) không có nội dung thực thể. Trong trường hợp này, phương thức DeleteProduct
có kiểu trả về là void
, vì vậy ASP.NET Web API sẽ tự động dịch mã này thành mã trạng thái 204 (No Content).