[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.
Bài viết này sử dụng Visual Studio 2022.
Trước tiên chúng ta tạo một ứng dụng .NET 7 dạng console:

Đặt cho nó một cái tên:

Nhớ là .NET 7 nhé, trong bài này mình không dùng top-level statements, nếu bạn dùng thì nó sẽ hơi khác một chút.

Bấm nút phải chuột vào tên project và chọn “Manage NuGet packages”

Gõ vào ô tìm kiếm MiniWebServer.Server, chọn gói tương ứng và nhấn Install để cài đặt:

Nó sẽ hiện cửa sổ yêu cầu chấp nhận License, bấm I Accept để tiếp tục:

Cài đặt thêm gói Microsoft.Extensions.Caching.Memory.

Và cả gói Microsoft.Extensions.Logging.Console.

Vậy là đã cài đặt xong các gói cần thiết, giờ ta sẽ viết code:
Xóa dòng Console.WriteLine(“Hello, World!”); và thay thế bằng:
IServerBuilder serverBuilder = new MiniWebServerBuilder();
ConfigureServerServices(serverBuilder.Services);
var options = new ServerOptions()
{
BindingOptions = new BindingOptions[]
{
new BindingOptions()
{
Port = 8080,
}
}
};
serverBuilder.UseOptions(options);
Đoạn code trên tạo một server builder, với một cổng 8080, hàm ConfigureServerServices ta sẽ viết sau.
Tiếp theo, tạo một MiniApp, một MiniApp là một đơn vị xử lý, gắn liền với một HostName.
MiniAppBuilder appBuilder = new(serverBuilder.Services);
IMiniApp app = appBuilder.Build();
Viết một hàm xử lý cho một request:
app.MapGet("/", (context, cancellationToken) =>
{
context.Response.Content = new MiniWebServer.MiniApp.Content.StringContent("Hello world!");
return Task.CompletedTask;
});
Trong hàm xử lý, bạn có thể đọc các tham số trên query string, form… thông qua đối tượng context.Request, bạn cũng có thể sử dụng Session (nếu có đăng ký Session middleware) hay lấy thông tin người dùng (nếu có đăng ký Authentication middleware)… Sau đó bạn gán dữ liệu trả về cho response vào thuộc tính Content.
Sau đó ta gán MiniApp vừa tạo vào cho một host:
serverBuilder.AddHost(string.Empty, app);
Ở đây ta dùng string.Empty là tên host, có nghĩa là app của bạn sẽ được gọi với bất kỳ host name nào. Nếu muốn phục vụ cho riêng tên miền http://www.mini-web-server.com, thì bạn có thể gọi: serverBuilder.AddHost(“www.mini-web-server.com”, app);
Giờ thì build server và chạy:
var server = serverBuilder.Build();
server.Start();
Thêm hàm ConfigureServerServices để khởi tạo các đối tượng cần thiết:
private static void ConfigureServerServices(IServiceCollection services)
{
services.AddLogging(loggingBuilder => loggingBuilder.AddConsole());
services.AddDistributedMemoryCache();
services.AddTransient<IHttpComponentParser, ByteSequenceHttpParser>();
services.AddTransient<IProtocolHandlerFactory, ProtocolHandlerFactory>();
services.AddSingleton<IMimeTypeMapping>(StaticMimeMapping.Instance);
}
Vậy là xong, giờ ta sẽ chạy thử:

Mở một cửa số trình duyệt và thử truy cập vào server:

Yeah! Vậy là ta đã có một web server đơn giản. Trong cửa số MiniHelloworld, bạn sẽ thấy server đã phục vụ một số request.

Thường thì các trình duyệt hay request thêm một số thứ, file favicon chẳng hạn, do vậy có thể có nhiều hơn một request được gửi lên. Sau vài giây bạn sẽ thấy dòng Connection expired xuất hiện, đó là vì chúng ta chờ một chút xem client có gửi gì nữa không, nếu không thì ta sẽ tự động đóng lại (chế động Connection=alive).
Toàn bộ source code của chương trình MiniHelloworld như sau:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MiniWebServer.Configuration;
using MiniWebServer.HttpParser.Http11;
using MiniWebServer.MiniApp;
using MiniWebServer.MiniApp.Builders;
using MiniWebServer.MiniWebServer.MimeMapping;
using MiniWebServer.Server;
using MiniWebServer.Server.Abstractions;
using MiniWebServer.Server.Abstractions.Parsers.Http11;
namespace MiniHelloworld
{
internal class Program
{
static void Main(string[] args)
{
IServerBuilder serverBuilder = new MiniWebServerBuilder();
ConfigureServerServices(serverBuilder.Services);
var options = new ServerOptions()
{
BindingOptions = new BindingOptions[]
{
new BindingOptions()
{
Port = 8080,
}
}
};
serverBuilder.UseOptions(options);
MiniAppBuilder appBuilder = new(serverBuilder.Services);
IMiniApp app = appBuilder.Build();
app.MapGet("/", (context, cancellationToken) =>
{
context.Response.Content = new MiniWebServer.MiniApp.Content.StringContent("Hello world!");
return Task.CompletedTask;
});
serverBuilder.AddHost(string.Empty, app);
var server = serverBuilder.Build();
server.Start();
}
private static void ConfigureServerServices(IServiceCollection services)
{
services.AddLogging(loggingBuilder => loggingBuilder.AddConsole());
services.AddDistributedMemoryCache();
services.AddTransient<IHttpComponentParser, ByteSequenceHttpParser>();
services.AddTransient<IProtocolHandlerFactory, ProtocolHandlerFactory>();
services.AddSingleton<IMimeTypeMapping>(StaticMimeMapping.Instance);
}
}
}
Đừng quên vào trang github (https://github.com/daohainam/mini-web-server/) và tặng một ngôi sao nhé!
Cũng đừng quên xem những clip hấp dẫn bổ ích tại: https://www.youtube.com/@hoc-lap-trinh