# ASP.NET Core Minimal APIs 2026: Kiến Trúc, Hiệu Năng và Câu Hỏi Phỏng Vấn > Hướng dẫn toàn diện về ASP.NET Core Minimal APIs với kiến trúc hiện đại, tối ưu hiệu năng Native AOT và các câu hỏi phỏng vấn thường gặp cho developer .NET. - Published: 2026-07-24 - Updated: 2026-07-24 - Author: SharpSkill - Reading time: 5 min --- ASP.NET Core Minimal APIs loại bỏ phần lớn boilerplate code từ các MVC controller truyền thống, mang đến cách tiếp cận gọn nhẹ hơn để xây dựng HTTP endpoints. Được giới thiệu trong .NET 6 và liên tục cải tiến qua .NET 8, 9 và hiện tại là .NET 10, Minimal APIs đã trưởng thành thành lựa chọn production-ready cho microservices, serverless functions và lightweight web services. > **Minimal APIs vs Controllers** > > Minimal APIs sử dụng top-level route handlers được định nghĩa trực tiếp trong Program.cs, trong khi MVC controllers yêu cầu định nghĩa class, attributes và convention-based routing. Đối với các thao tác CRUD đơn giản hoặc microservices với ít hơn 20 endpoints, Minimal APIs thường giảm code từ 40-60%. ## Kiến Trúc Minimal API và Request Pipeline Request pipeline của ASP.NET Core xử lý HTTP requests thông qua các middleware components trước khi đến endpoint handlers. Minimal APIs tích hợp liền mạch với pipeline này đồng thời cung cấp cú pháp khai báo rõ ràng hơn cho việc định nghĩa route. ```csharp // Program.cs var builder = WebApplication.CreateBuilder(args); // Register services for dependency injection builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddScoped(); var app = builder.Build(); // Middleware pipeline configuration app.UseExceptionHandler("/error"); app.UseHttpsRedirection(); app.UseAuthorization(); // Minimal API endpoint definitions app.MapGet("/products", async (IProductRepository repo) => Results.Ok(await repo.GetAllAsync())); app.MapGet("/products/{id:int}", async (int id, IProductRepository repo) => await repo.GetByIdAsync(id) is Product product ? Results.Ok(product) : Results.NotFound()); app.Run(); ``` Mẫu thiết kế này tập trung các định nghĩa route trong khi vẫn duy trì quyền truy cập đầy đủ vào dependency injection container. Tham số `IProductRepository` minh họa constructor-less injection trực tiếp vào handler delegates. ## Route Groups và Tổ Chức Endpoint Khi ứng dụng phát triển, việc tổ chức endpoints trở nên quan trọng. Route groups, được giới thiệu trong .NET 7, cung cấp namespacing và cấu hình chung mà không ảnh hưởng đến cách tiếp cận tối giản. ```csharp // ProductEndpoints.cs public static class ProductEndpoints { public static void MapProductEndpoints(this WebApplication app) { var group = app.MapGroup("/api/products") .WithTags("Products") .RequireAuthorization(); group.MapGet("/", GetAllProducts); group.MapGet("/{id:int}", GetProductById); group.MapPost("/", CreateProduct) .Accepts("application/json") .Produces(StatusCodes.Status201Created); group.MapPut("/{id:int}", UpdateProduct); group.MapDelete("/{id:int}", DeleteProduct) .RequireAuthorization("AdminOnly"); } private static async Task GetAllProducts( IProductRepository repo, CancellationToken ct) { var products = await repo.GetAllAsync(ct); return Results.Ok(products); } private static async Task GetProductById( int id, IProductRepository repo, CancellationToken ct) { var product = await repo.GetByIdAsync(id, ct); return product is not null ? Results.Ok(product) : Results.NotFound(); } private static async Task CreateProduct( CreateProductRequest request, IProductRepository repo, IValidator validator, CancellationToken ct) { var validation = await validator.ValidateAsync(request, ct); if (!validation.IsValid) return Results.ValidationProblem(validation.ToDictionary()); var product = await repo.CreateAsync(request.ToProduct(), ct); return Results.Created($"/api/products/{product.Id}", product); } } ``` Việc gọi `app.MapProductEndpoints()` trong Program.cs đăng ký tất cả product routes với shared authorization requirements và OpenAPI metadata. Cấu trúc này có khả năng mở rộng tốt cho các ứng dụng có hàng trăm endpoints. ## Parameter Binding và Validation Minimal APIs hỗ trợ nhiều binding sources: route parameters, query strings, headers, request bodies và services từ DI. Hiểu rõ binding precedence giúp tránh những sai lầm phổ biến trong phỏng vấn. ```csharp // Program.cs - Parameter binding examples app.MapGet("/search", ( [FromQuery] string? query, // Explicit query string [FromQuery] int page = 1, // Default value [FromQuery] int pageSize = 20, // Default value [FromHeader(Name = "X-Correlation-Id")] string? correlationId, ILogger logger) => { logger.LogInformation("Search request: {Query}, Page: {Page}, CorrelationId: {CorrelationId}", query, page, correlationId); return Results.Ok(new { query, page, pageSize, correlationId }); }); // Complex model binding with validation app.MapPost("/orders", async ( [FromBody] CreateOrderRequest request, [FromServices] IValidator validator, [FromServices] IOrderService orderService, HttpContext context, CancellationToken ct) => { var validationResult = await validator.ValidateAsync(request, ct); if (!validationResult.IsValid) { return Results.ValidationProblem( validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())); } var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier); var order = await orderService.CreateOrderAsync(request, userId!, ct); return Results.Created($"/orders/{order.Id}", order); }); ``` Attribute `[FromBody]` là tùy chọn cho complex types nhưng cải thiện khả năng đọc code. FluentValidation tích hợp tự nhiên thông qua dependency injection, tách biệt logic validation khỏi endpoint handlers. > **Thứ Tự Ưu Tiên Binding Source** > > Khi không có attribute nào được chỉ định, Minimal APIs suy luận binding sources: route parameters trước, sau đó query strings cho simple types, và request body cho complex types. Các explicit attributes như [FromQuery] hoặc [FromBody] ghi đè hành vi này. ## Tối Ưu Hiệu Năng với Native AOT .NET 8 giới thiệu hỗ trợ Native AOT (Ahead-of-Time) compilation cho Minimal APIs, tạo ra các executable độc lập với thời gian khởi động dưới một mili-giây. Khả năng này làm cho Minimal APIs trở nên lý tưởng cho serverless deployments nơi cold start latency rất quan trọng. ```csharp // Program.cs - AOT-compatible configuration var builder = WebApplication.CreateSlimBuilder(args); // AOT-friendly JSON serialization builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default); }); var app = builder.Build(); app.MapGet("/health", () => Results.Ok(new HealthResponse("Healthy", DateTime.UtcNow))); app.Run(); // Source-generated JSON serializer context [JsonSerializable(typeof(HealthResponse))] [JsonSerializable(typeof(Product))] [JsonSerializable(typeof(List))] internal partial class AppJsonContext : JsonSerializerContext { } public record HealthResponse(string Status, DateTime CheckedAt); ``` Method `CreateSlimBuilder` loại trừ các tính năng framework không cần thiết, trong khi source-generated `JsonSerializerContext` loại bỏ runtime reflection cho JSON serialization. Published AOT binaries cho simple APIs thường có kích thước 10-15 MB so với 80+ MB cho standard self-contained deployments. ## Typed Results và Response Metadata .NET 7 giới thiệu `TypedResults` cho việc xác minh compile-time của response types, cải thiện độ chính xác của tài liệu OpenAPI và phát hiện type mismatches trong quá trình development. ```csharp // Strongly-typed results with OpenAPI metadata app.MapGet("/products/{id:int}", async Task, NotFound, ProblemHttpResult>> ( int id, IProductRepository repo, CancellationToken ct) => { try { var product = await repo.GetByIdAsync(id, ct); return product is not null ? TypedResults.Ok(product) : TypedResults.NotFound(); } catch (Exception ex) { return TypedResults.Problem( detail: "An error occurred retrieving the product", statusCode: StatusCodes.Status500InternalServerError); } }) .WithName("GetProductById") .WithOpenApi(operation => { operation.Summary = "Retrieves a product by ID"; operation.Description = "Returns the product details or 404 if not found"; return operation; }); ``` Union type `Results` khai báo tất cả các response types có thể có, được Swagger/OpenAPI generators sử dụng để tạo tài liệu chính xác. Pattern này đặc biệt có giá trị khi chuẩn bị cho các câu hỏi phỏng vấn về API design. ## Endpoint Filters cho Cross-Cutting Concerns Endpoint filters cung cấp chức năng tương tự middleware được giới hạn cho các endpoints hoặc groups cụ thể, xử lý các concerns như logging, caching và request transformation. ```csharp // ValidationFilter.cs public class ValidationFilter : IEndpointFilter where T : class { public async ValueTask InvokeAsync( EndpointFilterInvocationContext context, EndpointFilterDelegate next) { var validator = context.HttpContext .RequestServices .GetService>(); if (validator is null) return await next(context); var argument = context.Arguments .OfType() .FirstOrDefault(); if (argument is null) return await next(context); var validationResult = await validator.ValidateAsync(argument); if (!validationResult.IsValid) { return Results.ValidationProblem( validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())); } return await next(context); } } // Usage in Program.cs app.MapPost("/products", CreateProduct) .AddEndpointFilter>(); // Global filter registration via route group var api = app.MapGroup("/api") .AddEndpointFilter(async (context, next) => { var logger = context.HttpContext .RequestServices .GetRequiredService>(); var stopwatch = Stopwatch.StartNew(); var result = await next(context); stopwatch.Stop(); logger.LogInformation( "Endpoint {Method} {Path} completed in {ElapsedMs}ms", context.HttpContext.Request.Method, context.HttpContext.Request.Path, stopwatch.ElapsedMilliseconds); return result; }); ``` Các filters được thực thi theo thứ tự đăng ký, với innermost filter gần nhất với endpoint handler. Kiến trúc này cho phép tách biệt rõ ràng logic validation, logging và authorization. ## Các Pattern Authentication và Authorization Minimal APIs hỗ trợ các cơ chế authentication và authorization tương tự như MVC controllers, với cú pháp cấu hình khai báo hơn. ```csharp // Program.cs - JWT authentication setup builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)) }; }); builder.Services.AddAuthorizationBuilder() .AddPolicy("AdminOnly", policy => policy.RequireRole("Admin")) .AddPolicy("PremiumUser", policy => policy.RequireClaim("subscription", "premium", "enterprise")); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); // Protected endpoints app.MapGet("/admin/users", async (IUserService userService) => Results.Ok(await userService.GetAllUsersAsync())) .RequireAuthorization("AdminOnly"); app.MapGet("/profile", async (ClaimsPrincipal user, IUserService userService) => { var userId = user.FindFirstValue(ClaimTypes.NameIdentifier); var profile = await userService.GetProfileAsync(userId!); return Results.Ok(profile); }) .RequireAuthorization(); // Anonymous endpoint within protected group var protectedGroup = app.MapGroup("/api/secure") .RequireAuthorization(); protectedGroup.MapGet("/public-info", () => Results.Ok("This is public")) .AllowAnonymous(); ``` Extension method `RequireAuthorization` chấp nhận tên policy hoặc có thể được gọi không có đối số để yêu cầu bất kỳ user đã xác thực nào. Hiểu các patterns này rất quan trọng cho các câu hỏi phỏng vấn về authentication và authorization. ## Câu Hỏi Phỏng Vấn: Các Pattern Phổ Biến Các cuộc phỏng vấn kỹ thuật thường khám phá sự khác biệt giữa Minimal APIs và traditional controllers. Bảng dưới đây tóm tắt các điểm khác biệt chính: | Khía Cạnh | Minimal APIs | MVC Controllers | |-----------|--------------|----------------| | Boilerplate | Thấp - handler delegates trực tiếp | Cao hơn - class + method + attributes | | Routing | Inline với `MapGet`, `MapPost` | Attribute hoặc convention-based | | Model Binding | Tự động với optional attributes | Convention + attributes | | Filters | Endpoint filters | Action filters + middleware | | Hỗ trợ AOT | Đầy đủ từ .NET 8 | Hạn chế, reflection-heavy | | Testability | Function-based, dễ unit test | Yêu cầu khởi tạo controller | | Phù hợp cho | Microservices, simple APIs | Ứng dụng lớn, complex workflows | > **Mẹo Phỏng Vấn** > > Khi được hỏi "Khi nào nên chọn controllers thay vì Minimal APIs?", hãy đề cập đến các ứng dụng phức tạp yêu cầu action filters, tùy chỉnh model binding, hoặc codebases hiện có với các patterns MVC đã được thiết lập. Minimal APIs xuất sắc cho greenfield microservices và serverless functions. ## Testing Các Endpoint Minimal API Integration testing với `WebApplicationFactory` cung cấp xác minh endpoint thực tế mà không cần deploy ứng dụng. ```csharp // ProductEndpointsTests.cs public class ProductEndpointsTests : IClassFixture> { private readonly HttpClient _client; private readonly WebApplicationFactory _factory; public ProductEndpointsTests(WebApplicationFactory factory) { _factory = factory.WithWebHostBuilder(builder => { builder.ConfigureServices(services => { // Replace real repository with mock services.RemoveAll(); services.AddScoped(); }); }); _client = _factory.CreateClient(); } [Fact] public async Task GetProducts_ReturnsOkWithProductList() { // Act var response = await _client.GetAsync("/api/products"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var products = await response.Content .ReadFromJsonAsync>(); products.Should().NotBeNull(); products.Should().HaveCountGreaterThan(0); } [Fact] public async Task GetProductById_WithInvalidId_ReturnsNotFound() { // Act var response = await _client.GetAsync("/api/products/99999"); // Assert response.StatusCode.Should().Be(HttpStatusCode.NotFound); } [Fact] public async Task CreateProduct_WithValidRequest_ReturnsCreated() { // Arrange var request = new CreateProductRequest("Test Product", 29.99m, "Test Description"); // Act var response = await _client.PostAsJsonAsync("/api/products", request); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); response.Headers.Location.Should().NotBeNull(); } } ``` Để khám phá sâu hơn về clean architecture patterns trong các ứng dụng .NET, service layer nên được test độc lập với unit tests, trong khi integration tests xác minh toàn bộ request pipeline. ## Kết Luận Minimal APIs đại diện cho cách tiếp cận hiện đại để xây dựng HTTP services trong ASP.NET Core: - Route groups và tổ chức endpoint có khả năng mở rộng từ microservices đơn giản đến các ứng dụng phức tạp - Endpoint filters cung cấp sự tách biệt rõ ràng của cross-cutting concerns như validation và logging - TypedResults cho phép xác minh compile-time response type và tài liệu OpenAPI chính xác - Native AOT compilation mang đến thời gian khởi động dưới mili-giây cho serverless deployments - Integration testing với WebApplicationFactory đảm bảo xác minh endpoint thực tế Để chuẩn bị phỏng vấn, hãy tập trung vào việc trình bày khi nào Minimal APIs phù hợp so với traditional controllers, và thể hiện sự hiểu biết về request pipeline, dependency injection và authentication patterns. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/dotnet/aspnet-core-minimal-apis-architecture-performance-interview