Phân Tích Nguồn gốc Web API: Đăng ký Route

Học hỏi từ khung WebAPI, phương pháp "đầu ra ép buộc đầu vào" vẫn là cách hiệu quả. Dù bài viết có tốt hay không, hãy kiên trì, chắc chắn sẽ có kết quả. Bài viết này khá dài, bạn cần vài phút để đọc và suy ngẫm kỹ lưỡng.

Làm việc với .NET đã nhiều năm, từ WebForm cũ kỹ đến MVC, và giờ đây là Web API trong mô hình tách biệt frontend-backend với phong cách RESTful, tôi tin rằng nhiều người đã từng sử dụng các khung web này. Chúng ta có thể coi đó là một phần phát triển của Web trên nền .NET. Tuy nhiên, nhiều công nghệ dần trở nên lỗi thời do sự phát triển, thay đổi và sự phổ biến của mô hình tách biệt frontend-backend. Hiện nay có quá nhiều khung web phổ biến, không chỉ giới hạn ở .NET mà còn rất xuất sắc, ví dụ:

  • Python: Django | Flask
  • Node: Express và Koa

Quan trọng nhất không phải là bạn đã dùng bao nhiêu, biết bao nhiêu, mà là bạn đã tích lũy được gì. Mặc dù một số công nghệ có thể đã lỗi thời trong lĩnh vực sử dụng, nhưng về mặt kỹ thuật, tư duy thiết kế khung, phong cách mã nguồn, cách sử dụng các điểm kỹ thuật, thậm chí cả cách khai báo biến, đều xứng đáng để chúng ta học hỏi.

Cách chia sẻ

  1. Đăng ký Route và Xử lý Route trong Web API: Sẽ được giải thích thông qua việc đọc mã nguồn và các ghi chú.
  2. Tích hợp và Mở rộng Pipeline Web API: Mặc dù chúng ta sẽ tiếp xúc với nó khi đọc mã nguồn, tôi vẫn sẽ sử dụng các ví dụ `Demo` để minh họa.
  3. Tích hợp và Mở rộng Pipeline Web API: Tôi thực sự nghĩ rằng thiết kế của nó rất tuyệt, không thể không đề cập riêng, dù có thể tự làm mình bối rối, nhưng tôi vẫn muốn thử.

1. Giới thiệu về Route trong Web API

1.1. Giới thiệu cơ bản

Một ứng dụng Web ASP.NET có một bảng định tuyến toàn cục, được biểu diễn bởi một thuộc tính tĩnh Routes có kiểu RouteCollection trong lớp RouteTable.

Tại sao lại như vậy? Có thể cách giải thích này không dễ hiểu lắm, hãy xem một ví dụ đơn giản. Chúng ta sẽ sửa đổi cách đăng ký route khi khởi động khung API, thay vì sử dụng phương thức do khung cung cấp, chúng ta sẽ thêm route trực tiếp vào RouteTable.Routes. Cách làm này tương đương với cách khung cung cấp, cuối cùng đều thêm route vào cùng một nơi.

Điểm này không chỉ áp dụng cho WebAPI, mà còn cho WebForm và MVC, bạn cũng có thể thêm dữ liệu trực tiếp vào bảng định tuyến khi ứng dụng khởi động. Tuy nhiên, ở đây chỉ để chứng minh mô tả trên, việc bạn có nên sử dụng trong phát triển hàng ngày hay không hoàn toàn phụ thuộc vào bạn.

`1. Cách khung cung cấp`

public static void Cau_hinh_Routes(HttpConfiguration cau_hinh)
{
    // Route do khung Web API cung cấp
    cau_hinh.Routes.MapHttpRoute(
        ten: "DefaultApi",
        mau_route: "api/{controller}/{action}/{id}",
        mac_dinh: new { id = RouteParameter.Optional }
    );
}

`2. Thêm trực tiếp vào bảng định tuyến`

// Sửa đổi trong WebApiConfig.Cau_hinh_Routes để đăng ký route.
public static void Cau_hinh_Routes(HttpConfiguration cau_hinh)
{
    // Xác minh route có được thêm vào bảng định tuyến toàn cục
    var mac_dinh = new { id = RouteParameter.Optional };
    // Lấy bộ xử lý route mặc định của WebAPI
    IRouteHandler bo_xu_ly = HttpControllerRouteHandler.Instance;
    RouteValueDictionary gia_tri_mac_dinh = new RouteValueDictionary(mac_dinh);
    Route route = new Route("api/{controller}/{action}/{id}", gia_tri_mac_dinh, bo_xu_ly);
    RouteTable.Routes.Add("DefaultApi", route);
}

1.2. Các câu hỏi cần suy nghĩ

  1. Tại sao RouteCollection lại là static?

Vì đối tượng static sẽ tồn tại trong bộ nhớ cho đến khi pool ứng dụng được thu gom rác lần tiếp theo.

  1. Khi chúng ta phát triển ứng dụng API bằng WebAPI, URL yêu cầu từ phía frontend được định tuyến đến bộ điều khiển (controller) và action tương ứng như thế nào?

Thực tế là nó được kiểm soát bởi route. Cách nó được kiểm soát và thực hiện sẽ được giới thiệu dần dần sau này.

  1. Route kiểm soát định tuyến như thế nào?

1. Đăng ký route trước, sau đó liên kết route với bộ xử lý.
2. Sau đó, yêu cầu của người dùng sẽ được so khớp với bộ xử lý tương ứng dựa trên URL yêu cầu, và bộ xử lý sẽ phân tích quy tắc mẫu route.
3. Dựa trên các quy tắc đã phân tích, sử dụng reflection để tìm thấy Controller và Action.

2. Đăng ký Route trong Web API

Phần đăng ký route này trong Web API có 2 phần kể từ MVC5, đó là route dựa trên thuộc tính (attribute routing), nhưng trong bài viết này tôi sẽ không đề cập, có thể sẽ bổ sung trong các bài viết sau. Hãy xem sơ đồ đăng ký route đơn giản, tất nhiên vẫn còn nhiều thứ khác, nhưng tôi chỉ vẽ một cách sơ sài.

Trong quy trình được mô tả ở trên, chúng ta, với tư cách là nhà phát triển sử dụng khung, chỉ cần quan tâm đến một phần nhỏ. Về mặt mã nguồn, chúng ta chỉ thấy một vài dòng mã đơn giản, và thậm chí cả đoạn mã này cũng được khung tạo ra, điều này cũng cho thấy khung được đóng gói rất hoàn chỉnh và mạnh mẽ, giúp nhà phát triển chỉ tập trung vào logic kinh doanh của mình.

2.1. Phần đăng ký route

1. Cách đăng ký

1. Khi ứng dụng Web API khởi động, trước tiên nó sẽ gọi phương thức GlobalConfiguration.Configure(WebApiConfig.Cau_hinh_Routes). Phương thức này nhận một Action<HttpConfiguration> làm tham số. Khi thấy Action, phản ứng đầu tiên của chúng ta là nó được thực thi như một callback. Khung đã dành sẵn không gian mở rộng cho người dùng, nội dung mở rộng của người dùng sẽ được thực thi bên trong.

2. Nói một cách đơn giản, tham số của phương thức GlobalConfiguration.Configure() cần một delegate, và bản chất của delegate là một phương thức không có giá trị trả về, chứa một instance của kiểu HttpConfiguration làm tham số. Chúng ta chuyển đến định nghĩa của khung WebApiConfig.Cau_hinh_Routes(), không nghi ngờ gì nó phù hợp với yêu cầu, vì vậy phương thức này chính là callback được thực thi bởi GlobalConfiguration.Configure() trong khung, và tác dụng của callback này là đăng ký route.

`Đăng ký route như một callback của khung GlobalConfiguration.Configure, tương đương với hình trên`

2. GlobalConfiguration

Chúng ta sẽ tìm thấy nhiều thông tin hơn trong lớp GlobalConfiguration. Hãy cùng phân tích từng bước.

1. Nó là một lớp static chứa một phương thức quan trọng Configure(Action<HttpConfiguration> configurationCallback)
2. Nó chứa 3 thuộc tính static quan trọng Configuration, DefaultHandler, DefaultServer
3. Ba thuộc tính này được khởi tạo khi được gọi, nhưng được bao bọc bởi kiểu Lazy, điều này có nghĩa là chúng được thực thi chậm (lazy execution), thời điểm thực thi chậm cụ thể là khi gọi thuộc tính Value của chúng.
4. Hiện tại trong giai đoạn đăng ký route, tôi chỉ giới thiệu Configuration, hai thuộc tính còn lại sẽ được chia sẻ trong phần phân tích route.

1. Đầu tiên, hãy xem GlobalConfiguration.Configure(Action<HttpConfiguration> configurationCallback) đã làm gì bên trong. Nó thực hiện 2 việc: thực thi callback mà chúng ta mở rộng, tức là logic đăng ký route, sau đó kiểm tra và khởi tạo HttpConfiguration.

Tham số cần thiết để thực thi callback chính là thuộc tính đầu tiên Configuration. Thông qua phương thức mở rộng Routes của kiểu này, chúng ta có thể thêm dữ liệu vào bảng định tuyến. Có thể thấy rằng chỉ cần chúng ta hiểu rõ nguồn gốc của kiểu HttpConfiguration, chúng ta có thể hiểu được nhiều thứ.

Hãy tìm xem HttpConfiguration kiểu Configuration được khởi tạo như thế nào. Đầu tiên, hãy mở rộng nó và xem mã nguồn, chúng ta thấy nó được tạo bởi phương thức CreateConfiguration() bên trong.

2.2. Các bước trong quy trình đăng ký

1. Trong CreateConfiguration(), khi HttpConfiguration được xây dựng, sau khi xem xét ngữ cảnh, chúng ta phát hiện:

1. Nhận một đối tượng HostedHttpRouteCollection và gán nó cho thuộc tính _routes có kiểu HttpRouteCollection bên trong.
2. Khi HostedHttpRouteCollection được xây dựng, nó nhận bảng định tuyến toàn cục RouteTable.Routes của chúng ta.

2. Chúng ta tìm đến phương thức mở rộng MapHttpRoute và biết rằng khi gọi nó, thuộc tính Route là một HostedHttpRouteCollection. Điều này có nghĩa là trong MapHttpRoute, CreateRoute() được gọi bởi HostedHttpRouteCollection.

3. Tiếp tục chuyển đến bên trong phương thức CreateRoute() của lớp HostedHttpRouteCollection, chúng ta thấy nó trả về một HostedHttpRoute, và đó là instance thực hiện IHttpRoute.

4. Tiếp tục đào sâu, khi HostedHttpRoute được xây dựng, bên trong có một thuộc tính OriginalRoute có kiểu Route, nó được gán giá trị là kiểu HttpWebRoute (kế thừa từ Route : RouteBase).

5. Khi HttpWebRoute được khởi tạo, nó nhận các tham số rất quan trọngmẫu route và một IRouteHandler có kiểu HttpControllerRouteHandler cùng với một IHttpRoute có kiểu HostedHttpRoute, sau đó chương trình trả về. Lưu ý rằng IRouteHandler ở đây có thể hiểu là bộ xử lý route. Cuối cùng, trên đối tượng HostedHttpRouteCollection gọi `routes.Add(ten, route)` để thêm tên mẫu routebộ xử lý.

3. Tóm tắt

Bây giờ chúng ta đã có một cái nhìn đơn giản về phần đăng ký route trong Web API và từng bước hiểu được lý luận triển khai mã nguồn. Thực chất, việc đăng ký route chỉ làm một việc, đó là liên kết trước mẫu quy tắc route và bộ xử lý route. Client sẽ so khớp theo quy tắc yêu cầu tương ứng để tìm bộ xử lý route phù hợp để xử lý cuối cùng, và bộ xử lý route tích hợp trong khung hiện tại là một HttpControllerRouteHandler. Phần tiếp theo sẽ đi vào xử lý route. Nếu bạn có bất kỳ thắc mắc nào trong quá trình đọc, hãy thảo luận với tôi bất cứ lúc nào. Tôi khuyên mạnh rằng sau khi đọc chia sẻ này và xây dựng được các kiến thức cơ bản, nếu có thời gian, hãy tự mình tải mã nguồn Web API để đọc, điều này sẽ giúp bạn hiểu tốt hơn.

Thẻ: asp.net webapi Routing HttpConfiguration SourceCode

Đăng vào ngày 5 tháng 7 lúc 04:29