In Out Middleware

Mọi người, trong video này tôi sẽ dạy bạn mọi thứ bạn cần biết về Middleware. Chúng ta sẽ bắt đầu từ từ bằng cách học một vài khái niệm lập trình hàm, những khái niệm cốt lõi để hiểu cách Middleware hoạt động, và sau đó chúng ta sẽ xây dựng pipeline riêng của mình, điều này sẽ tăng độ khó lên khá nhiều từ đầu nhưng khi chúng ta đạt được, chúng ta sẽ thư giãn với việc xem xét một chút về ASP.NET Middleware, cách chúng làm việc tại middleware của HTTP client, chúng ta sẽ xem xét một số interceptors cho Axios và Angular HTTP client, và cũng sẽ chỉ để vui vẻ làm cùng một pipeline middleware mà chúng ta sẽ triển khai trong C# nhưng bằng JavaScript. Được rồi, hãy bắt đầu với tutorial Middleware C#.

Chúng ta sẽ bắt đầu bằng cách học một vài paradigms lập trình hàm và cơ bản nhất là chúng ta có thể coi một hàm như một biến. Vậy cách chúng ta lưu trữ một số nguyên chúng ta có thể lưu trữ một hàm như một biến. Đây là khái niệm chính mà chúng ta sẽ cố gắng hiểu hôm nay. Và tại sao bạn muốn lưu trữ một hàm như một biến? Đầu tiên, hãy tạo một hàm mà chúng ta có thể thực thi. Viết public void FirstFunction, nơi chúng ta sẽ nói rằng đang thực thi FirstFunction và hãy tiến hành dump nó. Chúng ta sẽ xuất nó như thế này. Nhân tiện, nếu bạn không biết chương trình này là gì, đây gọi là LINQPad. Tôi sẽ để một liên kết trong phần mô tả nếu bạn muốn tải xuống. Về cơ bản, chúng ta có một hàm được gọi là First, những gì chúng ta muốn làm tiếp theo là hãy nói rằng đây là một hàm hoàn toàn không cần thiết nhưng hãy bao bọc nó với Start và End. Khi chúng ta xuất, chúng ta có thể thấy khi hàm bắt đầu và khi nó kết thúc. Hãy tạo một hàm thứ hai, và chúng ta sẽ xem hàm thứ hai này đang thực thi như thế nào. Chúng ta sẽ sao chép nó và đặt nó xuống đây và nói rằng chúng ta sẽ làm điều tương tự nhưng với hàm thứ hai.

Vậy nên, toàn bộ ý tưởng về lập trình là chúng ta đang cố gắng giảm bớt sự lặp lại, rất nhiều sự lặp lại là xấu và bạn thực sự đang lặp lại chính mình, bạn đang làm gấp đôi công việc, bạn không muốn làm gấp đôi công việc, bạn muốn làm việc thông minh hơn. Sự lặp lại chính là việc một hàm được bao bọc với Start và End. Vậy nên đây là nơi chúng ta muốn truyền một hàm như một tham số vào một hàm khác, về cơ bản là bao bọc một số công việc vào một công việc khác. Vậy nên hàm là thứ bạn sẽ làm và hãy nói rằng mỗi khi bạn làm điều này, bạn cũng muốn làm điều này, nhưng sau đó hãy nói rằng bạn có thể làm điều đó nhưng trước khi làm điều này cũng làm điều đó.

Vậy nên, hãy nói rằng chúng ta muốn bao bọc và chúng ta sẽ lấy hàm này ở đây, chúng ta sẽ thay thế nó bằng một hàm khác và vì chúng ta sẽ truyền nó như một biến, chúng ta sẽ sử dụng chữ thường nhưng chúng ta không có nó ở đây và cách bạn gọi một hàm là bạn gọi dấu ngoặc đơn, vậy làm sao chúng ta truyền hàm như một tham số vào hàm này? Có hai cách: Cách cũ là sử dụng delegates, vốn giống như một interface cho một hàm. Vậy nên chúng ta có thể tạo một public delegate, và delegate này thực chất giống như một interface, đúng vậy, delegate cho một hàm, giống như bạn tạo một interface, đây là cách bạn tạo một interface cho một hàm và sau đó bạn xác định chữ ký của hàm đó, chữ ký của hàm là có bao nhiêu tham số và kiểu trả về là gì. Vậy nên bây giờ chúng ta đã tạo một interface để bao bọc hàm, vậy nên chúng ta có thể nói rằng chúng ta đang mong đợi một ToWrap ở đây và điều này sẽ là một hàm mà chúng ta có thể thực thi.

Vậy nên chúng ta đang mong đợi truyền các hàm này vào đây và lý do chúng ta có thể truyền chúng là vì interface ToWrap nói rằng chúng ta đang mong đợi một hàm với chữ ký không có tham số và trả về void. Vậy nên hãy gọi Wrap trên hàm thứ hai trước đây và sau đó truyền hàm thứ hai vào đây và điều này hoạt động vì chúng ta không gọi hàm này, chúng ta chỉ truyền nó như một tham số, nghĩa là chỉ tên của hàm và điều này cơ bản là truyền hàm như một tham số. Ngay khi chúng ta gọi hàm, nó sẽ thực thi First trước khi thực thi hàm này, nghĩa là nếu nó thực thi ở đây, nó trả về void nên chúng ta không muốn trả về void, chúng ta muốn trả về hàm, vậy nên chúng ta đã làm điều đó và bây giờ chúng ta có thể tạo hàng tá các hàm khác và cho mỗi hàm bổ sung mà chúng ta thêm vào, chúng ta cơ bản giảm bớt hai dòng mã. Về mặt khác, cách khác để cung cấp một hàm như một tham số là sử dụng một mẫu mới hơn gọi là sử dụng một loại gọi là Action, nó hơi mới hơn và dường như được sử dụng ở khắp nơi, không ai còn sử dụng delegates nữa. Vậy nên chúng ta không còn sử dụng delegates nữa, chúng ta nói rằng chúng ta mong đợi một Action và điều này biên dịch đúng, vậy nên điều này hoạt động và giả sử chúng ta muốn truyền một tham số vào đây vì Action nói rằng chúng ta mong đợi một hàm void, vì vậy không có kiểu trả về, nếu chúng ta cung cấp một Func thì điều đó có nghĩa là nó phải trả về một int. Vậy nên những gì chúng ta cung cấp ở đây phải trả về một int và bây giờ điều này biên dịch nhưng chúng ta không cung cấp một Func hoặc chúng ta không trả về gì cả nên nó đang phàn nàn ở đây. Vì vậy, chúng ta không sẽ không chạm vào các Func nhưng cơ bản là hiểu rằng Actions không trả về gì, vậy nên điều duy nhất là bạn có thể điền vào Action là những thứ trả về void, và nếu chúng ta muốn một chữ ký có một tham số, ví dụ như nếu chúng ta truyền một int, bạn có thể thấy điều này không còn phù hợp nữa, vậy nên chúng ta có thể thêm một int vào đây, nghĩa là một hàm mong đợi một int, vậy thì chúng ta có thể làm tương tự nếu chúng ta có nhiều hơn một tham số. Bạn có thể thấy nó không còn biên dịch nữa, vậy nên đó cơ bản là về Action, chúng ta sẽ giữ ví dụ đơn giản hơn ở đây. Hãy chạy lại và bây giờ chúng ta sẽ bao bọc hàm Try.

Hãy tạo một hàm Try, chúng ta sẽ nói rằng chúng ta đang bao bọc hàm này bên trong một try-catch, chúng ta sẽ gọi hàm và chúng ta sẽ bắt ngoại lệ, chúng ta không ném ngoại lệ nhưng chúng ta chỉ đảm bảo rằng chúng ta đang bắt nó. Vậy nên chúng ta có một chút try-catch. Vậy làm thế nào chúng ta có thể đặt hàm Wrap vào đây vì chữ ký ở đây không còn phù hợp với chữ ký cho Action này nữa, nhưng vấn đề là nếu chúng ta thay đổi chữ ký của hàm Try thành loại Action, chúng ta thực sự đang phá vỡ chuỗi các hàm này, khả năng thay đổi các hàm này vì chữ ký bắt đầu khác nhau và đó là phần cốt lõi chính của Middleware là khả năng thay đổi các thành phần Middleware với cùng một chữ ký để thực thi các triển khai khác nhau.

Vậy nên, hãy truyền chúng vào hàm Wrap thông qua các biểu thức lambda và sau đó chúng ta sẽ xem lý do tại sao điều đó có thể là vấn đề. Vậy nên, với hàm Try, chúng ta sẽ truyền vào một lambda, chúng ta sẽ làm tương tự cho Wrap. Chạy lại và chúng ta thấy rằng chúng ta đang thử trước khi thực thi hàm đầu tiên và sau đó thực thi hàm kết thúc.

Điều quan trọng ở đây là Middleware trong lập trình hàm là khả năng thay đổi thứ tự thực thi của các hàm. Vấn đề chính ở đây là vào thời điểm này, chữ ký của các thành phần Middleware phải giống nhau để chúng có thể được xâu chuỗi lại với nhau một cách dễ dàng. Nếu chúng khác nhau, chúng ta phải sử dụng các biện pháp phụ trợ như lambda để điều chỉnh chữ ký, điều này làm mã trông xấu và khó bảo trì.

Vì vậy, điểm chính của Middleware là chúng ta chỉ đang bao bọc các hàm khác nhau lại với nhau với cùng một chữ ký, cho phép chúng ta dễ dàng thêm, thay đổi hoặc sắp xếp lại các thành phần Middleware mà không phải thay đổi quá nhiều trong mã của chúng ta.

Tiếp theo, chúng ta sẽ sử dụng lập trình hướng đối tượng để tái cấu trúc lại các Middleware, làm cho chúng dễ dàng xâu chuỗi hơn và loại bỏ sự phụ thuộc vào các lambda như một biện pháp phụ trợ.

Sau khi xây dựng xong pipeline Middleware, chúng ta có thể dễ dàng thêm vào các Middleware mới hoặc thay đổi thứ tự chúng mà không làm phức tạp thêm mã của chúng ta.

Bây giờ, hãy chuyển sang Visual Studio và xem cách triển khai Middleware tùy chỉnh trong ASP.NET Core. Chúng ta sẽ tạo một Middleware tùy chỉnh bằng cách tạo một lớp và triển khai hàm Invoke hoặc InvokeAsync, giống như chúng ta đã làm trong ví dụ LINQPad. Middleware của ASP.NET Core sử dụng RequestDelegate và HTTP Context để xử lý các yêu cầu và phản hồi, tương tự như cách chúng ta đã xây dựng Middleware trong ví dụ của mình.

Tương tự, chúng ta có thể triển khai Middleware trong các thư viện HTTP client như Axios và Angular HTTP Client bằng cách sử dụng các interceptors, những thành phần này cho phép chúng ta can thiệp vào quá trình gửi và nhận yêu cầu HTTP, thực hiện các thao tác trước và sau khi xử lý yêu cầu, giống như Middleware.

Cuối cùng, chúng ta sẽ triển khai một phiên bản Middleware đơn giản trong JavaScript để hiểu rõ hơn về cách thức hoạt động của Middleware bằng cách tạo một pipeline đơn giản mà các hàm Middleware có thể xâu chuỗi lại với nhau để xử lý một yêu cầu.

Tóm lại, Middleware là một khái niệm mạnh mẽ trong lập trình, cho phép chúng ta xâu chuỗi các hàm xử lý lại với nhau để thực hiện các tác vụ phức tạp một cách linh hoạt và dễ bảo trì. Bằng cách hiểu rõ về các paradigms lập trình hàm và cách thức hoạt động của Middleware, bạn sẽ có thể xây dựng các ứng dụng có cấu trúc tốt và dễ dàng mở rộng.

Cảm ơn rất nhiều vì đã xem video. Nếu bạn thích nó, đừng quên nhấn like và đăng ký kênh. Nếu bạn có bất kỳ câu hỏi nào, hãy để lại chúng trong phần bình luận hoặc đến hỏi trên server Discord của tôi. Tôi cũng livestream vào các ngày thứ Tư và Chủ nhật, hãy tham gia Twitch của tôi qua liên kết trong phần mô tả. Chúc bạn một ngày tốt lành, hy vọng sẽ gặp bạn trong các video khác của tôi.

Nhận xét

Bài đăng phổ biến từ blog này

Generics

Channels

Dependency Injection