Hướng dẫn tuyệt vời về cách xây dựng API RESTful với ASP.NET Core
Tổng quát
RESTful không phải là một thuật ngữ mới. Nó đề cập đến một phong cách kiến trúc nơi các dịch vụ web nhận và gửi dữ liệu từ và đến các ứng dụng client.Mục tiêu của các dịch vụ web này là tập trung dữ liệu mà các ứng dụng client khác nhau sẽ sử dụng.
Chọn đúng công cụ để viết các dịch vụ RESTful là rất quan trọng vì chúng ta cần quan tâm đến khả năng mở rộng, bảo trì, tài liệu và tất cả các khía cạnh liên quan khác.
Trong bài viết này, tôi cũng sẽ chỉ cho bạn cách viết một RESTful API có cấu trúc tốt cho một kịch bản thực tế, sử dụng ASP.NET Core. Tôi sẽ trình bày chi tiết các pattern và chiến lược chung để đơn giản hóa quá trình phát triển.
Tôi cũng sẽ chỉ cho bạn cách tích hợp các framework và thư viện phổ biến, chẳng hạn như Entity Framework Core và AutoMapper, để cung cấp các chức năng cần thiết.
Điều kiện tiên quyết
Tôi mong bạn có kiến thức về các khái niệm lập trình hướng đối tượng.
Tôi sẽ trình bày nhiều về ngôn ngữ lập trình C#, tôi khuyên bạn nên có kiến thức cơ bản về chủ đề này.
Tôi cũng giả sử bạn đã biết REST là gì, giao thức HTTP hoạt động như thế nào, API Endpoint là gì và JSON là gì. Đây là một hướng dẫn giới thiệu tuyệt vời về chủ đề này. Yêu cầu cuối cùng là bạn phải hiểu cơ sở dữ liệu quan hệ hoạt động như thế nào.
Để viết mã cùng với tôi, bạn sẽ phải cài đặt .NET Core 3.1 , cũng như Postman, công cụ tôi sẽ sử dụng để kiểm tra API. Tôi khuyên bạn nên sử dụng trình soạn thảo Visual Studio Code để phát triển API, tuy nhiện bạn hoàn toàn có thể chọn trình soạn thảo bạn thích. Nếu bạn chọn trình soạn thảo này, tôi khuyên bạn nên cài đặt phần mở rộng C# để có hiển thị code tốt hơn.
Bạn có thể tìm thấy liên kết đến kho Github của API ở cuối bài viết này để kiểm tra kết quả cuối cùng.
Phạm vi
Tạo một RESTful API cho siêu thị. Hãy tưởng tượng chúng ta phải triển khai phạm vi sau:
- Tạo một dịch vụ RESTful cho phép các ứng dụng của khách hàng quản lý danh mục sản phẩm của siêu thị. Nó cần hiển thị các điểm cuối để tạo, đọc, chỉnh sửa và xóa các danh mục sản phẩm, chẳng hạn như sản phẩm sữa và mỹ phẩm, đồng thời cũng để quản lý các sản phẩm thuộc các danh mục này.
- Đối với danh mục, chúng ta cần lưu trữ tên của chúng. Đối với sản phẩm, chúng ta cần lưu tên của chúng, đơn vị đo lường (ví dụ: KG đối với sản phẩm được đo bằng trọng lượng), số lượng trong gói (ví dụ: 10 nếu sản phẩm là một gói bánh quy) và danh mục tương ứng của chúng.
Để đơn giản hóa ví dụ, tôi sẽ không xử lý sản phẩm tồn kho, vận chuyển sản phẩm, bảo mật và bất kỳ chức năng nào khác. Phạm vi đã cho đủ để cho bạn thấy ASP.NET Core hoạt động như thế nào.
Để phát triển dịch vụ này, về cơ bản chúng ta cần hai API Endpoint: một để quản lý danh mục và một để quản lý sản phẩm. Về giao tiếp JSON, chúng ta có thể nghĩ về các phản hồi như sau:
API Endpoint: /api/categories
Mẫu JSON trả về (đối với GET request):
{
[
{ "id": 1, "name": "Fruits and Vegetables" },
{ "id": 2, "name": "Breads" },
… // Other categories
]
}
API Endpoint:/api/products
Mẫu JSON trả về (đối với GET request):
{
[
{
"id": 1,
"name": "Sugar",
"quantityInPackage": 1,
"unitOfMeasurement": "KG"
"category": {
"id": 3,
"name": "Sugar"
}
},
… // Other products
]
}
Hãy bắt đầu viết ứng dụng.
Bước 1 – Tạo API
Trước hết, chúng ta phải tạo cấu trúc thư mục cho dịch vụ web, sau đó chúng ta phải sử dụng công cụ .NET CLI để xây dựng một API web cơ bản. Mở cửa sổ dòng lệnh (tùy thuộc vào hệ điều hành bạn đang sử dụng) và nhập các lệnh sau theo trình tự:
mkdir src/Supermarket.API
cd src/Supermarket.API
dotnet new webapi
Hai lệnh đầu tiên để tạo thư mục mới cho API và di chuyển tới thư mục mới đó. Lệnh cuối cùng tạo một dự án mới theo mẫu Web API, đó là loại ứng dụng chúng ta sẽ xây dựng. Bạn có thể đọc thêm về lệnh này và các mẫu dự án khác mà bạn có thể tạo bằng cách xem liên kết này .
Thư mục mới bây giờ sẽ có cấu trúc sau:
Tổng quan về cấu trúc
Một ứng dụng ASP.NET Core bao gồm một nhóm các middleware (các phần nhỏ của ứng dụng được gắn vào đường ống ứng dụng – request pipeline, xử lý các yêu cầu và phản hồi) được cấu hình trong lớp Startup
. Nếu bạn đã từng làm việc với framework Express.js trước đây, thì khái niệm này không còn mới đối với bạn.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.SetCompatibilityVersion(CompatibilityVersion.Latest);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Khi ứng dụng khởi chạy, phương thức Main
trong lớp Program
được gọi. Nó tạo một máy chủ web mặc định bằng cách sử dụng cấu hình khởi chạy, hiển thị ứng dụng qua HTTP thông qua một cổng cụ thể (theo mặc định, cổng 5000 cho HTTP và 5001 cho HTTPS).
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Hãy xem lớp ValuesController
bên trong thư mục Controllers
. Nó có các phương thức sẽ được gọi khi API nhận được yêu cầu thông qua định tuyến /api/values
.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody] string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
Đừng lo lắng nếu bạn không hiểu một số phần của mã này. Tôi sẽ trình bày chi tiết từng cái khi phát triển các điểm cuối API cần thiết. Bây giờ, chỉ cần xóa lớp này, vì chúng tôi sẽ không sử dụng nó.
Bước 2 – Tạo Domain Model
Tôi sẽ áp dụng một số khái niệm thiết kế để giữ cho ứng dụng đơn giản và dễ bảo trì.
Việc viết mã có thể hiểu và tự duy trì không khó, nhưng bạn phải nhớ rằng bạn sẽ làm việc như một phần của nhóm. Nếu bạn không quan tâm đến cách bạn viết mã của mình, kết quả sẽ là một con quái vật khiến bạn và đồng đội của bạn phải đau đầu liên tục. Nghe có vẻ cực đúng không? Nhưng tin tôi đi, đó là sự thật.
Hãy bắt đầu bằng cách viết tầng domain. Tầng này sẽ chứa các lớp model đại diện cho sản phẩm và danh mục của chúng ta, cũng như các repository và service interface. Tôi sẽ giải thích hai khái niệm cuối cùng này trong một thời gian.
Bên trong thư mục Supermarket.API
, tạo một thư mục mới có tên Domain
. Trong thư mục Domain
tạo một thư mục khác có tên Models
. Model đầu tiên chúng ta phải thêm vào thư mục này là Category
. Ban đầu, nó sẽ là một lớp Plain Old CLR Object (POCO) đơn giản. Nó có nghĩa là lớp sẽ chỉ có thuộc tính để mô tả thông tin cơ bản của nó.
using System.Collections.Generic;
namespace Supermarket.API.Domain.Models
{
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Product> Products { get; set; } = new List<Product>();
}
}
Lớp Category
có một thuộctính Id
để định danh cho danh mục và một thuộc tính Name
. Lớp cũng có một thuộctính Products
sẽ được Entity Framework Core sử dụng. Entity Framework Core là một ứng dụng ORM được sử dụng để thao tác với cơ sở dữ liệu, giúp ánh xạ mối quan hệ giữa danh mục và sản phẩm. Nó cũng có ý nghĩa tư duy về mặt lập trình hướng đối tượng, vì một danh mục có nhiều sản phẩm liên quan.
Tiếp theo chúng ta sẽ phải tạo ra lớp model cho sản phẩm. Tại cùng một thư mục Models
, hãy thêm lớp Product
như sau:
namespace Supermarket.API.Domain.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public short QuantityInPackage { get; set; }
public EUnitOfMeasurement UnitOfMeasurement { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
}
Lớp Product
cũng có các thuộc tính Id
và Name
. Ngoài ra còn có thuộc tính QuantityInPackage
cho biết có bao nhiêu đơn vị sản phẩm trong một gói và thuộc tính UnitOfMeasurement
– đơn vị tính, cái này có kiểu enum. Hai thuộc tính cuối cùng là CategoryId
và Category
sẽ được ORM sử dụng để lập bản đồ mối quan hệ giữa sản phẩm và danh mục. Nó chỉ ra rằng một sản phẩm chỉ thuộc một danh mục.
Hãy xác định phần cuối cùng của các mô hình miền của chúng ta,enum EUnitOfMeasurement
.
using System.ComponentModel;
namespace Supermarket.API.Domain.Models
{
public enum UnitOfMeasurement : byte
{
[Description("UN")]
Unity = 1,
[Description("MG")]
Milligram = 2,
[Description("G")]
Gram = 3,
[Description("KG")]
Kilogram = 4,
[Description("L")]
Liter = 5
}
}
Mã thực sự đơn giản. Ở đây chúng tôi chỉ xác định một số khả năng cho các đơn vị đo lường, tuy nhiên, trong một hệ thống siêu thị thực, bạn có thể có nhiều đơn vị đo lường khác và có thể là một mô hình riêng cho điều đó.
Lưu ý thuộc tính được áp dụng cho mọi khả năng liệt kê. Thuộc tính là một cách để xác định siêu dữ liệu trên các lớp, giao diện, thuộc tính và các thành phần khác của ngôn ngữ C #. Trong trường hợp này, chúng tôi sẽ sử dụng nó để đơn giản hóa các phản hồi của điểm cuối API sản phẩm, nhưng bạn không phải quan tâm đến nó lúc này. Chúng tôi sẽ quay lại đây sau.Description
Các mô hình cơ bản của chúng tôi đã sẵn sàng để sử dụng. Bây giờ chúng ta có thể bắt đầu viết điểm cuối API sẽ quản lý tất cả các danh mục.
Bước 3 – Tạo API danh mục
Trong thư mục Controllers, thêm lớp mới có tên là CategoriesController
.
Theo quy ước, tất cả các lớp trong thư mục này kết thúc bằng hậu tố Controller
sẽ trở thành controller của ứng dụng của chúng ta. Nó có nghĩa là chúng sẽ xử lý các yêu cầu và trả về phản hồi. Bạn phải kế thừa lớp này từ lớp ControllerBase
, được định nghĩa trong namespace Microsoft.AspNetCore.Mvc
.
Controller mới sẽ xử lý các yêu cầu và trả về các phản hồi thông qua định tuyến /api/categories
. Chúng ta đạt được điều này bằng cách thêmthuộc tính Route
phía trên tên lớp, để chỉ định một trình giữ chỗ cho biết rằng định tuyến sẽ sử dụng tên lớp mà không có hậu tố controller theo quy ước.
using Microsoft.AspNetCore.Mvc;
namespace Supermarket.API.Controllers
{
[Route("/api/[controller]")]
public class CategoriesController : Controller
{
}
}