Hướng dẫn học .NET

👉 Tìm hiểu về .NET: Bạn ít nhất phải biết .NET là gì (không biết thì đọc ở đây: https://daohainam.com/2023/10/18/gioi-thieu-ve-net/), cách nó chạy chương trình như thế nào, rồi tiếp đến tìm hiểu cách viết, dịch và chạy chương trình. Ít nhất bạn phải biết bạn làm được gì với nó, cách tải và cài đặt Visual Studio (dùng hẳn bản Visual Studio nhé, đừng dùng Visual Studio Code, tập đạp xe thì cứ kiếm cái xe bình thường mà tập, đừng bắt đầu bằng xe một bánh), cách tạo project, biên dịch, chạy và debug.

Bạn có thể học .NET bằng cách theo dõi các video bài học của một tác giả có hơn 20 năm kinh nghiệm .NET tại đây: https://www.youtube.com/playlist?list=PLRLJQuuRRcFlaITD5F6XKQJxOt8QgCNAg

👉 Học ngôn ngữ lập trình: C# là ngôn ngữ chính thống nhất và đầy đủ nhất trên .NET, cú pháp đơn giản, thân thiện với người đã biết C++, Java nên học rất nhanh. Bắt đầu bằng các chương trình console, giải các bài tập đơn giản để quen với cú pháp của nó.

Bạn nên lập ra một danh sách các bài tập cần viết trước khi bắt đầu, đặt mục tiêu hoàn thành là khi bạn làm hết các bài đó. Danh sách bài tập có thể tham khảo từ các sách học ngôn ngữ khác.

Continue reading “Hướng dẫn học .NET”

Viết một OpenIdConnect server

Hôm trước làm 2 clip về Authentication và Authorization xong nhưng vẫn cứ thấy thiếu thiếu, vì phần OAuth2 và OpenIdConnect (OIDC) mình nói hơi sơ sài, nghe lại cứ có cảm giác như nói cho xong bài hay chạy KPI vậy.

Làm cái gì mà không tới nơi tới chốn cảm giác nó rất bực mình, vậy nên nay lại ngồi cặm cụi viết một OpenIdConnect server, “from scratch” luôn, hi vọng khi nào xong mình sẽ làm một clip riêng về OAuth2, OIDC, sử dụng chương trình này làm demo, để mọi người thấy từng bước nó chạy, và id_token, access_token, refresh_token được tạo ra và quản lý như thế nào.

Continue reading “Viết một OpenIdConnect server”

Vài mẹo tối ưu trong .NET

– Ưu tiên dùng các hàm Try*: ví dụ như khi phân tích chuỗi thành số, thay vì dùng int.Parse, ta nên dùng int.TryParse, kiểm tra kết quả trả về của nó để biết việc chuyển có thành công hay không.

– Sử dụng IDictionary.TryGetValue thay vì dùng Items và bắt Exception.

– Sử dụng ArgumentNullException.ThrowIfNull() thay vì if (v == null) throw new ArgumentNullException().

– Sử dụng ArgumentOutOfRangeException.ThrowIf* thay vì if () throw new ArgumentOutOfRangeException().

– Khi cần một IEnumerable rỗng, hãy sử dụng Enumerable.Empty<T>() thay vì new List<T>(), tương tự cho Array.Empty.

– Trong khối catch (Exception ex), bạn có thể dùng throw; để throw lại ex mà vẫn giữ stack trace, nếu bạn gọi throw ex; thì stack trace sẽ được tạo lại từ nơi bạn gọi.

– Các object hay dùng nên được “cache” lại bằng cách sử dụng các static instance.

– Từ khóa stackalloc sẽ giúp bạn khai báo một biến trên stack thay vì trên heap, sử dụng nó rất tốt với các biến kích thước nhỏ trong các hàm được gọi nhiều, nhưng cũng phải cẩn trọng nếu hàm của bạn được gọi đệ quy vì nó dễ gây stack overflow. Bạn cũng cần tránh khai báo nó bên trong vòng lặp vì lý do tương tự.

* Exception là một thứ ‘đắt đỏ’ trong .NET, nó phải thu thập nhiều thông tin như stack trace, thông tin mã nguồn…, vì vậy không nên lạm dụng. Ta chỉ nên sử dụng new Exception trong những trường hợp thực sự là ngoại lệ, hiếm gặp.

* Quá trình dọn rác trên heap tiêu tốn rất nhiều tài nguyên, vậy nên càng ít tạo object mới càng tốt.

WebSocket đã sẵn sàng để test!

Sau 3 tuần thì Websocket middleware đã sẵn sàng. Phần khó nhất khi viết module này là làm sao xử lý request từ dạng request/response sang 1 socket cho phép gửi/nhận bất kỳ dạng dữ liệu nào. Nếu các bạn có theo dõi các commit ban đầu thì sẽ thấy mình đã phải thử một số cách khác nhau để chọn ra cách cuối cùng.

Giải pháp mình đưa ra là cung cấp một IWebSocketManager cho IMiniAppContext, IWebSocketManager mặc nhiên sẽ luôn trả về false khi gọi đến IsUpgradeRequest, và throw một InvalidOperationException nếu gọi đến AcceptAsync.

Khi các bạn thêm WebSocket middleware (gọi services.AddWebSocketService() và appBuilder.UseWebSockets()), nó sẽ theo dõi request và kiểm tra xem request đó có phải là một yêu cầu nâng cấp HTTP request lên websocket request không, nếu đúng là vậy thì nó sẽ thay thế IWebSocketManager mặc nhiên với DefaultWebSocketManager, DefaultWebSocketManager sẽ có nhiệm vụ gửi lại response cho client thông báo chấp nhận kết nối và đồng thời tạo một WebSocket cho phép app của bạn tương tác với client.

Continue reading “WebSocket đã sẵn sàng để test!”

Viết một app dựa trên Mini-Web-Server và Clean Architecture

Mỗi khi nghe đến SOLID hay Clean Architecture, nhiều người cứ nghĩ nó phải là cái gì đó phức tạp và khó hiểu lắm. Nhưng không, người ta luôn cố gắng làm cho mọi thứ thật đơn giản để càng nhiều người học được càng tốt, công nghệ càng dễ nắm bắt sẽ càng phổ biến. Clean Architecture cũng vậy, thực chất nó rất đơn giản và dễ hiểu, ngay cả trong cuốn sách “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” thì phần nói riêng về nó cũng chỉ đâu đó tầm chục trang (trong khi cả cuốn sách là hơn 300 trang). Tất nhiên, muốn hiểu về nó bạn cũng cần phải trang bị trước những kiến thức khác, nhưng tôi muốn khẳng định rằng, nó không hề phức tạp như bạn nghĩ.

Xin giới thiệu với các bạn 1 project nhỏ demo cho Clean Architecture, dựa trên Mini-Web-Server, sử dụng MVC, và được viết ngắn gọn hết sức có thể, hầu như mọi thứ “râu ria” đều bị loại bỏ, ngay cả những phần như xác thực dữ liệu cũng không có. Mục đích là để bạn có thể tập trung cho câu hỏi: Trong Clean Architecture, cái gì sẽ nằm ở đâu?

Các bạn có thể truy cập tại đây: https://github.com/daohainam/minishop

Nếu đọc thử một file Controller (https://github.com/…/Mini…/Controllers/HomeController.cs) nếu không để ý có lẽ các bạn sẽ nhầm đây là một controller của ASP.NET🙂.

Ghi chú:

– Phần giỏ hàng mình chưa làm, phần Product đã hoàn thành. Thật ra thì chỉ cần phần Product là đủ demo rồi.

– Template được dùng ở đây: https://startbootstrap.com/template/shop-homepage.

– Tên các project được đặt theo các lớp trong Clean Architecture để giúp các bạn dễ theo dõi (Entity, UseCase, Adapter).

– Sử dụng Repository pattern để phân tách phần truy xuất dữ liệu, phần này thuộc vào tầng Adapter, nhưng mình vẫn tách ra để dễ theo dõi hơn.

– Dữ liệu được lưu trong file https://github.com/…/master/MiniShop/Data/products.json, bạn không cần cài bất cứ phần mềm CSDL nào thêm để chạy được MiniShop. Tất nhiên nếu muốn bạn có thể tạo thêm một SqlServerProductRepository chẳng hạn, và thay thế nó với InMemoryProductRepository.

– Một số phần như Authentication và Session mình thêm vào sẵn nhưng chưa dùng tới, các bạn xóa đi cũng không sao (tìm trong file Program.cs).

Viết một web server đơn giản với Mini-Web-Server

[Lưu ý: Bài viết này được viết với .NET 7 và MiniWebServer 0.3.1, từ phiên bản 0.3.3 bạn sẽ cần dùng .NET 8]

Nếu theo dõi blog này chắc có lẽ bạn không lạ gì Mini-Web-Server, một máy chủ web 6 tháng tuổi, cung cấp nhiều tính năng với lượng code tối thiểu, giúp bạn có thể đọc và hiểu dễ dàng.

Tuy mục đích ban đầu là cung cấp một tập code mẫu trực quan cho một số môn học như OOP, phân tích thiết kế, lập trình mạng, hệ điều hành, lập trình hệ thống, .NET… nhưng sau một thời gian, Mini-Web-Server đã trở nên ổn định và nhiều tính năng hơn: Session, Authentication, Authorization, HTTPS, Caching, và thậm chí là cả một bộ MVC. Tất nhiên không thể so sánh được với các máy chủ web lớn phổ biến, nhưng nó hoàn toàn phục vụ được cho các web đơn giản, và sẽ rất thú vị khi bạn theo dõi những bước nó làm để phục vụ cho một yêu cầu từ người dùng: đọc các gói tin, phân tích thành một HTTP request, phân tích các thành phần trong request, tìm kiếm và chạy một trình xử lý cho request đó, lấy dữ liệu từ response và đẩy về client…

Một trong những ưu điểm giúp Mini-Web-Server dễ mở rộng là phần lõi được giữ tối thiểu, hầu hết các thành phần đều được thiết kế như các module mở rộng, ngay cả những thành phần quan trọng nhất như trình phân tích luồng dữ liệu cũng không nằm trong lõi, và nó hoàn toàn dễ dàng bị thay thế khi ta viết thêm các trình phân tích để hỗ trợ HTTP/2. Hầu hết tính năng nó có đều được cung cấp thông qua hệ thống Middleware, do vậy bạn chỉ cần cài những phần cần dùng, ở mức tối thiểu, nó chỉ chiếm vài MB bộ nhớ. Với định hướng trở thành một máy chủ web nhúng (chạy bên trong để mở rộng tính năng cho các ứng dụng khác), có lẽ chúng ta sẽ còn có thể tối ưu nó thêm nhiều nữa và cho phép nó chạy trên các thiết bị rất hạn chế tài nguyên.

Trong bài viết này, tôi sẽ hướng dẫn các bạn viết một máy chủ web đơn giản với Mini-Web-Server. Sau khi hoàn thành, bạn có thể gắn nó vào một chương trình sẵn có của bạn để cho phép người dùng truy cập đến thông qua giao diện web.

Continue reading “Viết một web server đơn giản với Mini-Web-Server”

Từ khóa yield trong C#

Hôm trước mình có một câu hỏi liên quan đến từ khóa yield (https://www.facebook.com/namdotnet/posts/pfbid02ghwNXv2WCxka7BUyYv4pW7jHJjzQrT1qNSZaLE3oWvv8dCWpjWc7jSS91L2UB78wl), sở dĩ mình nhắc đến từ khóa này vì nó liên quan đến khái niệm State Machine (máy trạng thái), cũng là một khái niệm được dùng để tạo nên async/await.

Lưu ý: đã có clip chi tiết về async/await

Continue reading “Từ khóa yield trong C#”

Open Close Principle – phần cuối

Các bạn có thể xem loạt clip về SOLID tại đây: https://www.youtube.com/playlist?list=PLRLJQuuRRcFlRei0t0wbbCdzU8ujTD3jE

Giờ ta đã có các business entity quan trọng nhất, cũng như định nghĩa ISolver, một “bản hướng dẫn” cách giải quyết vấn đề. Đúng vậy, ISolver là một interface, tự nó không thể giải được bài toán, nhưng nó đóng vai là bản cam kết, là một “contract” cho việc giải bài toán PTB1. Và đó cũng là nhiệm vụ của bất kỳ interface nào. Nguyên tắc đơn giản là: tôi không biết anh là ai, nhưng nếu anh implement ISolver, vậy chắc chắn anh có thể giải được bài toán này, anh có thể dùng đầu óc tính toán thông qua các phép cộng trừ nhân chia để tìm ra kết quả, hoặc như trong trường hợp của DistributedSolver (xem phần 2), anh phân phối lại các yêu cầu cho các máy tính khác và tổng hợp lại.

Vậy ta sẽ cần ít nhất một lớp hiện thực hóa ISolver, ta đặt tên nó là LocalCpuSolver (đặt tên hoành tráng một chút mới bán được nhiều tiền):

public interface ISolver {
Result Solve(Constants constants);
}
public class LocalCpuSolver implements ISolver {
    Result Solve(Constants constants) {
        return new Result() {
            ...
        };
    }
}

Tới đây, ta đã có một ISolver cụ thể (chính là LocalCpuSolver), hoàn toàn độc lập, bạn có thể viết các unit test và gắn vào để chạy, việc LocalCpuSolver chỉ phụ thuộc duy nhất vào ISolver giúp chúng ta dễ dàng kiểm thử, dễ dàng thay thế các instance của nó với instance của một class khác. Một thiết kế có càng ít mối phụ thuộc càng dễ bảo trì, chỉnh sửa, thay đổi… vậy nên, khi thiết kế, hãy cố gắng làm mọi thứ thật đơn giản.

Tương tự, ta sẽ xây dựng hai lớp ConsoleResultWriter và ConsoleConstantsReader, cho các interface IResultWriter và ConstantsReader (xem lại bài 2).

Continue reading “Open Close Principle – phần cuối”

HỌ ĐÃ LÀM ĐIỀU ĐÓ NHƯ THẾ NÀO?

Đó là câu hỏi thường trực trong đầu mình kể từ ngày bắt đầu học máy tính.

Làm thế nào mình gõ vài dòng code mà máy tính lại hiểu được mình muốn làm gì?

Khi mình gõ một nút trên bàn phím, họ làm thế nào để biết mình gõ chữ gì và làm sao đọc nó vào được?

Làm sao chuỗi ký tự từ câu lệnh printf lại chạy ra được màn hình?

Khi mình bấm nút nguồn thì quá trình khởi động bắt đầu, mà quá trình khởi động này nó làm gì nhỉ?

Làm sao khi mình gõ một địa chỉ thì nó biết được cần phải kết nối với máy tính nào? Và làm sao gửi được yêu cầu đến đúng máy đó mà không phải máy khác?

Continue reading “HỌ ĐÃ LÀM ĐIỀU ĐÓ NHƯ THẾ NÀO?”

HTTP/2 Rapid Reset Attack

Một trong những vấn đề bảo mật được nhắc đến nhiều nhất gần đây là HTTP/2 Rapid Reset Attack, được dùng để tạo ra các cuộc tấn công DDOS, với báo cáo lớn nhất là 394 triệu yêu cầu được gửi đến mỗi giây.

Tui nhân dịp này tranh thủ giải thích về lỗi bảo mật này, giúp các bạn hiểu thêm về giao thức HTTP, và hiểu về code của dự án Mini-Web-Server dễ dàng hơn.

Cách một request được xử lý trong HTTP/1.1

Khi một client gửi request đến một HTTP server, các bước thực hiện sẽ như sau:

– Mở kết nối TCP/IP đến HTTP server.

– Gửi request.

– Server phân tích request.

– Server xử lý request và trả về response.

– Client đọc response và xử lý kết quả.

Mỗi một vòng như vậy gọi là 1 round trip, client sẽ phải chờ cho đến khi server hoàn thành việc xử lý và trả về kết quả. Trong lúc server xử lý request, ví dụ như đọc file, cập nhật database… thì các phần khác cũng phải dừng lại. Client muốn gửi request kế tiếp thì phải chờ cho đến khi request hiện tại xử lý xong, do vậy không tận dụng hết tài nguyên của cả client và server.

Continue reading “HTTP/2 Rapid Reset Attack”