Ngân hàng câu hỏi

Ngân hàng câu hỏi ASP.NET MVC và Web API

80+ câu hỏi ASP.NET MVC 5 tới ASP.NET Core 8/9/10, Minimal API, versioning, rate limiting, output caching. Phỏng vấn fresher tới senior.
Cách dùng: tự trả lời ra giấy, sau đó so với đáp án. Câu có ⭐ là hay hỏi nhất.Level: 🟢 Intern | 🔵 Junior | 🟡 Mid | 🔴 Senior

A. MVC Pattern — Nền tảng

1. 🟢 MVC là gì? 3 thành phần?

Model-View-Controller — pattern tách ứng dụng thành 3 phần:

  • Model: dữ liệu và business logic. Không biết về UI.
  • View: giao diện — nhận data từ Model, render HTML. Không có business logic.
  • Controller: điều phối — nhận request, gọi Model, chọn View trả về.
HTTP Request
    ↓
Controller (nhận, xử lý)
    ↓
Model (lấy/tính toán data)
    ↓
Controller (chọn view + truyền data)
    ↓
View (render HTML)
    ↓
HTTP Response

2. 🟢 Lợi ích của MVC so với WebForms?

WebFormsMVC
Code-behindGắn liền HTML + C#Tách rõ
TestabilityKhó testDễ unit test
URLDạng file path (Default.aspx)Clean URL (/users/123)
HTML controlServer controls, viewstateFull control HTML
LifecyclePhức tạp (Page lifecycle)Đơn giản hơn

3. 🟢 ASP.NET MVC 5 khác ASP.NET Core MVC ra sao?

ASP.NET MVC 5 (.NET Framework)ASP.NET Core MVC
PlatformWindows onlyCross-platform
HostingIIS bắt buộcKestrel + IIS/Nginx/Apache
DICần lib ngoài (Unity, Autofac)Built-in
Configweb.config XMLJSON (appsettings.json)
MiddlewareHTTP Module/HandlerPipeline middleware
PerformanceChậm hơnNhanh hơn nhiều
StartupGlobal.asax + RouteConfig.csProgram.cs
Còn support.NET Framework 4.8 (EOL xa).NET 8/9/10 LTS

Câu hay hỏi: "Tại sao nên dùng ASP.NET Core thay vì MVC 5?" → Cross-platform, performance tốt hơn, DI built-in, module nhỏ hơn, cloud-native friendly.


B. Request Lifecycle — MVC 5

4. 🟡 Request lifecycle trong ASP.NET MVC 5?

Browser Request
    ↓
1. UrlRoutingModule           — match URL với route table
    ↓
2. MVC Route Handler          — xác nhận đây là MVC request
    ↓
3. MvcHandler + ControllerFactory — tạo instance Controller
    ↓
4. Action Invoker             — chọn Action method phù hợp
    ↓
5. Model Binding              — map request data → action parameters
    ↓
6. Action Filters (OnActionExecuting) — trước khi action chạy
    ↓
7. Action Method              — business logic, trả ActionResult
    ↓
8. Action Filters (OnActionExecuted) — sau khi action chạy
    ↓
9. Result Execution (View/JSON/Redirect)
    ↓
10. Result Filters            — sau khi result render
    ↓
Response

5. 🟡 Request lifecycle trong ASP.NET Core MVC?

Request
    ↓
Kestrel Server
    ↓
Middleware Pipeline (UseRouting → UseAuth → UseEndpoints)
    ↓
Endpoint Routing               — chọn controller/action
    ↓
Model Binding                  — bind route/query/body
    ↓
Action Filters (IActionFilter)
    ↓
Action Method
    ↓
Result Filters (IResultFilter)
    ↓
IActionResult.ExecuteAsync()   — render view/JSON
    ↓
Response

Khác MVC 5: ASP.NET Core không có HttpHandler/HttpModule — thay bằng Middleware pipeline. Mọi thứ đi qua IMiddleware chain.


C. Routing

6. 🟢 Conventional routing vs Attribute routing?

Conventional routing — khai báo template tập trung:

// Program.cs / RouteConfig.cs (MVC 5)
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Attribute routing — đặt ngay trên controller/action:

[Route("api/users")]
public class UsersController : ControllerBase {
    [HttpGet("{id:int}")]           // GET /api/users/42
    public IActionResult Get(int id) { ... }
    
    [HttpPost]                      // POST /api/users
    public IActionResult Create(CreateUserDto dto) { ... }
}

Web API nên dùng Attribute routing — rõ ràng, version URL dễ.

7. 🟢 Route constraint là gì? Ví dụ?

Ràng buộc giá trị parameter trong route:

[HttpGet("{id:int}")]          // id phải là số nguyên
[HttpGet("{slug:alpha}")]       // slug chỉ chứa chữ cái
[HttpGet("{age:range(1,120)}")] // age từ 1 đến 120
[HttpGet("{id:guid}")]          // id phải là GUID
[HttpGet("{name:minlength(3)}")] // name >= 3 ký tự

Nếu không match constraint → 404, không đi vào action.

8. 🔵 Endpoint routing (ASP.NET Core 3+) khác IRouter cũ ra sao?

Cũ (MVC 5 / Core 2): routing gắn liền với MVC handler. Middleware không biết route nào sẽ chạy.

Endpoint routing (Core 3+): routing tách thành 2 bước:

  1. UseRouting()chọn endpoint (gắn vào HttpContext.GetEndpoint()).
  2. UseAuthorization() / UseEndpoints() — middleware sau biết endpoint, có thể check auth TRƯỚC khi chạy action.

Lợi ích: middleware biết sớm endpoint → auth check chính xác hơn.


D. Controller và Action

9. 🟢 ActionResult các loại hay dùng?

return View();                          // ViewResult — render HTML
return View("MyView", model);           // ViewResult với view name + model
return Ok(data);                        // 200 JSON (Web API)
return Created(uri, data);              // 201 Created
return NoContent();                     // 204 No Content
return BadRequest("invalid");           // 400
return NotFound();                      // 404
return Unauthorized();                  // 401
return Forbid();                        // 403
return Redirect("/home");               // 302 Redirect
return RedirectToAction("Index");       // Redirect tới action
return Json(obj);                       // JSON (MVC 5 style)
return File(bytes, "image/png");        // File response
return Content("text", "text/plain");   // Plain text
return StatusCode(429);                 // Custom status

10. 🟢 IActionResult vs ActionResult<T> khác gì?

  • IActionResult: return bất kỳ loại result, không type-safe.
  • ActionResult<T>: generic — cũng return bất kỳ IActionResult, hoặc return T trực tiếp (sẽ tự wrap Ok(T)).
// IActionResult — không biết return gì
public IActionResult GetUser(int id) {
    var u = _service.Get(id);
    if (u == null) return NotFound();
    return Ok(u);
}

// ActionResult<T> — rõ ràng hơn, Swagger biết type
public ActionResult<UserDto> GetUser(int id) {
    var u = _service.Get(id);
    if (u == null) return NotFound();
    return u;        // implicit Ok(u)
}

11. 🔵 [ApiController] attribute làm gì?

  • Automatic model validation → BadRequest 400 nếu ModelState.IsValid == false.
  • Attribute routing required — không dùng conventional routing.
  • Binding source inference[FromBody] cho complex type, [FromRoute] cho route param tự động.
  • ProblemDetails response format theo RFC 7807.

12. 🟢 Model Binding hoạt động ra sao?

Framework tự map data từ request vào action parameters theo thứ tự:

  1. [FromRoute] — route params {id}.
  2. [FromQuery] — query string ?name=Alice.
  3. [FromBody] — JSON/form body.
  4. [FromHeader] — HTTP header.
  5. [FromForm] — form data.
[HttpPut("{id}")]
public IActionResult Update(
    int id,                          // FromRoute
    [FromQuery] bool notify,         // FromQuery
    [FromBody] UpdateUserDto dto)    // FromBody
{ ... }

E. Filters

13. 🔵 Filter trong ASP.NET Core — các loại?

FilterInterfaceKhi chạyDùng cho
AuthorizationIAuthorizationFilterTrước tất cảAuth check
ResourceIResourceFilterSau Auth, bao actionCaching, short-circuit
ActionIActionFilterTrước/sau actionLogging, validation
ExceptionIExceptionFilterKhi có exceptionError handling
ResultIResultFilterTrước/sau resultResponse transform

Thứ tự chạy:

Authorization → Resource → [Action → ActionMethod → Action] → [Result → ResultMethod → Result] → Exception

14. 🔵 Filter scope — đăng ký ở đâu?

// 1. Global — áp cho tất cả
builder.Services.AddControllers(options => {
    options.Filters.Add<GlobalExceptionFilter>();
});

// 2. Controller — áp cho tất cả action trong controller
[ServiceFilter(typeof(LoggingFilter))]
public class UsersController : ControllerBase { }

// 3. Action — chỉ action đó
[HttpGet]
[TypeFilter(typeof(RateLimitFilter))]
public IActionResult GetAll() { }

15. 🔵 Filter vs Middleware — khác gì?

MiddlewareFilter
LevelHTTP pipeline toàn appMVC pipeline
Biết về routing❌ (trừ dùng sau UseRouting)
Biết về action✅ (ActionContext)
Short-circuit
Dùng choCORS, Auth, logging chungValidation, caching, business logic

F. Views và Razor

16. 🟢 Razor syntax cơ bản?

@* Comment *@
@{ var name = "Alice"; }       @* Code block *@
<p>Hello @name</p>             @* Output biến *@
@if (Model.IsAdmin) {
    <span>Admin</span>
}
@foreach (var item in Model.Items) {
    <li>@item.Name</li>
}
@Html.ActionLink("Click", "Index", "Home")   @* HTML helper *@
<a asp-controller="Home" asp-action="Index">Click</a>  @* Tag helper *@

17. 🟢 ViewData vs ViewBag vs TempData vs Session?

ScopeTypeKhi nào
ViewData1 requestDictionary<string, object>Truyền data Controller → View
ViewBag1 requestdynamic (wrapper ViewData)Giống ViewData, cú pháp dynamic
TempData1 redirect (xong xoá)Dictionary<string, object>Thông báo sau redirect
SessionNhiều requestKey-valueUser session state
// Controller
ViewData["Name"] = "Alice";
ViewBag.Age = 25;
TempData["Success"] = "Saved!";

// View
<p>@ViewData["Name"]</p>
<p>@ViewBag.Age</p>
@if (TempData["Success"] != null) {
    <div class="alert">@TempData["Success"]</div>
}

18. 🔵 Partial View vs ViewComponent vs Display Template?

Dùng khiCó DI?Logic phức tạp?
Partial ViewTái sử dụng HTML fragment❌ (nhận ViewModel)
ViewComponentWidget độc lập (basket, cart, notification)
Display/Editor TemplateFormat kiểu dữ liệu (DateTime, Money)
// ViewComponent
public class CartSummaryViewComponent : ViewComponent {
    private readonly ICartService _cart;
    public CartSummaryViewComponent(ICartService cart) => _cart = cart;
    public IViewComponentResult Invoke() => View(_cart.GetSummary());
}

// Gọi trong View
@await Component.InvokeAsync("CartSummary")
// Hoặc
<vc:cart-summary />

19. 🟢 Layout trong Razor là gì?

Layout là template cha — define @RenderBody() + @RenderSection() cho phần động:

<!-- _Layout.cshtml -->
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    @await RenderSectionAsync("styles", required: false)
</head>
<body>
    <nav>...</nav>
    @RenderBody()          @* Content của page con *@
    @await RenderSectionAsync("scripts", required: false)
</body>
</html>

Page con dùng @section để điền vào:

@{
    Layout = "_Layout";
    ViewBag.Title = "Home";
}
@section styles {
    <link rel="stylesheet" href="~/css/home.css" />
}
<h1>Welcome</h1>

G. Model Validation

20. 🟢 Data Annotation validation?

public class CreateUserDto {
    [Required(ErrorMessage = "Name is required")]
    [StringLength(100, MinimumLength = 2)]
    public string Name { get; set; } = "";
    
    [Required]
    [EmailAddress]
    public string Email { get; set; } = "";
    
    [Range(18, 120)]
    public int Age { get; set; }
    
    [Phone]
    public string? Phone { get; set; }
    
    [Url]
    public string? Website { get; set; }
    
    [RegularExpression(@"^[A-Z]{2,}$", ErrorMessage = "Code phải là chữ hoa")]
    public string? Code { get; set; }
}

21. 🔵 ModelState.IsValid và FluentValidation?

// Manual check (không dùng [ApiController])
if (!ModelState.IsValid)
    return BadRequest(ModelState);

// FluentValidation — cho business rule phức tạp
public class CreateUserValidator : AbstractValidator<CreateUserDto> {
    public CreateUserValidator(IUserRepository repo) {
        RuleFor(x => x.Email).EmailAddress()
            .MustAsync(async (email, ct) => !await repo.ExistsAsync(email))
            .WithMessage("Email already taken");
        
        RuleFor(x => x.Age).GreaterThanOrEqualTo(18);
    }
}

FluentValidation tích hợp ASP.NET Core qua services.AddFluentValidation() — auto chạy trước action.


H. Web API

22. 🟢 REST API best practices?

  • URL là resource (danh từ): /api/users, /api/orders/{id}.
  • HTTP method = hành động: GET/POST/PUT/PATCH/DELETE.
  • Status code đúng: 200/201/204/400/401/403/404/422/500.
  • Versioning qua URL: /api/v1/users.
  • JSON camelCase response.
  • Paging: ?page=1&size=20.
  • Filtering: ?city=HCM&active=true.

23. ⭐🔵 Content Negotiation là gì?

Client gửi Accept header → server trả format phù hợp:

Accept: application/json          → trả JSON
Accept: application/xml           → trả XML (nếu support)
Accept: text/csv                  → trả CSV (custom formatter)
// Thêm XML support
builder.Services.AddControllers()
    .AddXmlSerializerFormatters();

// Custom formatter
builder.Services.AddControllers(options => {
    options.OutputFormatters.Add(new CsvOutputFormatter());
});

24. 🔵 Versioning API — các cách?

// Dùng Asp.Versioning.Http package

// 1. URL versioning (phổ biến nhất)
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/users")]
public class UsersV1Controller : ControllerBase { }

[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/users")]
public class UsersV2Controller : ControllerBase { }

// 2. Header versioning
// Request: Api-Version: 2.0

// 3. Query string
// Request: /api/users?api-version=2.0

// nuxt.config:
builder.Services.AddApiVersioning(opt => {
    opt.DefaultApiVersion = new ApiVersion(1, 0);
    opt.AssumeDefaultVersionWhenUnspecified = true;
    opt.ReportApiVersions = true;           // header: api-supported-versions
}).AddApiExplorer(opt => {
    opt.GroupNameFormat = "'v'VVV";
    opt.SubstituteApiVersionInUrl = true;
});

25. ⭐🔵 Rate Limiting (ASP.NET Core 7+ built-in)?

// Program.cs
builder.Services.AddRateLimiter(opt => {
    // Fixed window — 10 req / 10s per IP
    opt.AddFixedWindowLimiter("fixed", o => {
        o.PermitLimit = 10;
        o.Window = TimeSpan.FromSeconds(10);
        o.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        o.QueueLimit = 5;
    });
    
    // Sliding window — smoothes out traffic
    opt.AddSlidingWindowLimiter("sliding", o => {
        o.PermitLimit = 100;
        o.Window = TimeSpan.FromMinutes(1);
        o.SegmentsPerWindow = 4;
    });
    
    // Token bucket — cho burst
    opt.AddTokenBucketLimiter("token", o => {
        o.TokenLimit = 100;
        o.ReplenishmentPeriod = TimeSpan.FromSeconds(1);
        o.TokensPerPeriod = 10;
    });
    
    opt.RejectionStatusCode = 429;    // Too Many Requests
});

app.UseRateLimiter();

// Áp dụng per-endpoint
[EnableRateLimiting("fixed")]
[HttpGet]
public IActionResult GetAll() { }

26. 🔵 Output Caching (ASP.NET Core 7+ built-in)?

builder.Services.AddOutputCache(opt => {
    opt.AddBasePolicy(b => b.Expire(TimeSpan.FromMinutes(5)));
    opt.AddPolicy("ShortCache", b => b.Expire(TimeSpan.FromSeconds(30)));
});

app.UseOutputCache();

// Action
[HttpGet]
[OutputCache(Duration = 60)]
public IActionResult GetProducts() { }

// Với vary by query
[OutputCache(Duration = 120, VaryByQueryKeys = new[] { "category", "page" })]
public IActionResult GetByCategory(string category, int page = 1) { }

Khác ResponseCache: Output Cache store ở server (memory/Redis). ResponseCache chỉ set HTTP header Cache-Control để browser/CDN cache.

27. ⭐🔵 HybridCache (.NET 9 mới)?

// Combine in-memory (L1) + Redis (L2) + stampede protection
builder.Services.AddHybridCache();

// Inject
public class UserService {
    private readonly HybridCache _cache;
    public UserService(HybridCache cache) => _cache = cache;
    
    public async Task<User?> GetAsync(int id, CancellationToken ct = default) {
        return await _cache.GetOrCreateAsync(
            $"user:{id}",
            async token => await _db.Users.FindAsync(id, token),
            cancellationToken: ct);
    }
}

Lợi ích vs IMemoryCache + IDistributedCache:

  • Không cần tự viết stampede protection (cache stampede: nhiều request cùng lúc cache miss → cùng query DB).
  • Tự động L1 → L2 fallback.
  • Single API cho cả 2 level.

I. Minimal API (NET 6+)

28. ⭐🔵 Minimal API vs Controller — khi nào chọn?

// Minimal API — ít code hơn, nhanh hơn ~5-10% throughput
var app = WebApplication.Create();

app.MapGet("/users/{id:int}", async (int id, IUserService service) =>
    await service.GetByIdAsync(id) is User u ? Results.Ok(u) : Results.NotFound());

app.MapPost("/users", async (CreateUserDto dto, IUserService service) => {
    var created = await service.CreateAsync(dto);
    return Results.Created($"/users/{created.Id}", created);
});
Minimal APIController-based
VerbosityÍt codeNhiều boilerplate
Performance~5-10% nhanh hơnTốt
Tổ chức lớnKhó (dùng Route Groups)Dễ (class + method)
FiltersHạn chế (endpoint filter)Đầy đủ (Action, Result, Exception)
ConventionÍtNhiều (routing convention, naming)
Khi nàoMicroservice, minimal API, demoApp lớn, nhiều dev

29. 🔵 Route Groups trong Minimal API?

// Nhóm route có prefix + shared middleware
var users = app.MapGroup("/api/v1/users").RequireAuthorization();

users.MapGet("/", async (IUserService s) => await s.GetAllAsync());
users.MapGet("/{id:int}", async (int id, IUserService s) => {
    var u = await s.GetByIdAsync(id);
    return u is not null ? Results.Ok(u) : Results.NotFound();
});
users.MapPost("/", async (CreateUserDto dto, IUserService s) => {
    var created = await s.CreateAsync(dto);
    return Results.Created($"/api/v1/users/{created.Id}", created);
});

30. 🔵 Endpoint Filter (Minimal API)?

Tương đương Action Filter nhưng cho Minimal API:

app.MapPost("/users", CreateUser)
    .AddEndpointFilter<ValidationFilter<CreateUserDto>>();

public class ValidationFilter<T> : IEndpointFilter where T : class {
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext ctx, EndpointFilterDelegate next) {
        var dto = ctx.Arguments.OfType<T>().FirstOrDefault();
        if (dto is null) return Results.BadRequest("Invalid body");
        // Validate...
        return await next(ctx);
    }
}

J. Security

31. ⭐🔵 Authentication vs Authorization trong ASP.NET Core?

  • Authentication: "Bạn là ai?" — xác minh identity, populate HttpContext.User (ClaimsPrincipal).
  • Authorization: "Bạn được làm gì?" — check role/policy/claim.
// Middleware thứ tự BẮT BUỘC:
app.UseAuthentication();   // 1. Populate User
app.UseAuthorization();    // 2. Check User

// [Authorize] attribute
[Authorize]                                    // chỉ require login
[Authorize(Roles = "Admin,Manager")]           // require role
[Authorize(Policy = "MinAge18")]               // require policy
[AllowAnonymous]                               // bỏ qua auth

32. 🔵 JWT Authentication setup?

// Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(opt => {
        opt.TokenValidationParameters = new TokenValidationParameters {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = config["Jwt:Issuer"],
            ValidAudience = config["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(config["Jwt:Key"]!))
        };
    });

33. 🔵 Policy-based Authorization?

builder.Services.AddAuthorization(opt => {
    opt.AddPolicy("AdminOnly", p => p.RequireRole("Admin"));
    opt.AddPolicy("MinAge18", p => p.RequireClaim("age", "18", "19", "20"));
    opt.AddPolicy("PremiumUser", p =>
        p.Requirements.Add(new PremiumRequirement()));
});

// Custom requirement
public class PremiumRequirement : IAuthorizationRequirement {}
public class PremiumHandler : AuthorizationHandler<PremiumRequirement> {
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext ctx, PremiumRequirement req) {
        if (ctx.User.HasClaim("subscription", "premium"))
            ctx.Succeed(req);
        return Task.CompletedTask;
    }
}

// Dùng
[Authorize(Policy = "PremiumUser")]
public IActionResult PremiumContent() { }

34. 🔵 CORS setup?

builder.Services.AddCors(opt => opt.AddPolicy("default", policy =>
    policy.WithOrigins("https://app.com", "http://localhost:3000")
          .AllowAnyHeader()
          .AllowAnyMethod()
          .AllowCredentials()));

app.UseCors("default");  // Phải TRƯỚC UseAuthentication

K. Performance và Monitoring

35. 🔵 Response Compression?

builder.Services.AddResponseCompression(opt => {
    opt.EnableForHttps = true;      // cẩn thận BREACH attack với sensitive data
    opt.Providers.Add<BrotliCompressionProvider>();
    opt.Providers.Add<GzipCompressionProvider>();
});

app.UseResponseCompression();

36. 🟡 Health Check?

builder.Services.AddHealthChecks()
    .AddSqlServer(config.GetConnectionString("Default")!)
    .AddRedis(config.GetConnectionString("Redis")!)
    .AddCheck<CustomHealthCheck>("custom");

app.MapHealthChecks("/health", new HealthCheckOptions {
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

// Check endpoint liveness/readiness riêng
app.MapHealthChecks("/health/live", new HealthCheckOptions {
    Predicate = _ => false  // chỉ check app alive, không check DB
});

37. 🟡 Problem Details (RFC 7807)?

// Chuẩn hoá error response
builder.Services.AddProblemDetails();

// Kết quả khi BadRequest:
{
    "type": "https://httpstatuses.com/400",
    "title": "Bad Request",
    "status": 400,
    "detail": "Name is required",
    "instance": "/api/users",
    "traceId": "00-abc123..."
}

38. 🟡 Minimal API OpenAPI (NET 9+ — không cần Swashbuckle)?

// .NET 9+: Microsoft.AspNetCore.OpenApi
builder.Services.AddOpenApi();

app.MapOpenApi();    // GET /openapi/v1.json

// Với metadata
app.MapGet("/users/{id}", GetUser)
    .WithName("GetUser")
    .WithSummary("Get user by ID")
    .WithDescription("Returns user or 404")
    .Produces<UserDto>(200)
    .Produces(404)
    .WithTags("Users");

L. Câu hỏi hay + Scenario

39. ⭐ Sự khác nhau giữa MVC 5 và Core trong xử lý exception?

MVC 5:

// Global.asax / FilterConfig.cs
filters.Add(new HandleErrorAttribute());
// Dùng customErrors trong web.config

ASP.NET Core:

// Middleware — app-level
app.UseExceptionHandler("/error");
app.UseDeveloperExceptionPage();   // Dev only

// Problem Details middleware (NET 8+)
app.UseExceptionHandler();         // Auto ProblemDetails
app.UseStatusCodePages();

// Custom exception middleware
app.UseMiddleware<GlobalExceptionMiddleware>();

// Filter level
services.AddControllers(o => o.Filters.Add<GlobalExceptionFilter>());

40. ⭐ Khi nào dùng [FromBody] vs [FromForm]?

  • [FromBody]: JSON/XML payload. Request có Content-Type: application/json. Bắt buộc với complex type trong Web API.
  • [FromForm]: HTML form (Content-Type: application/x-www-form-urlencoded hoặc multipart/form-data). Dùng khi nhận file upload.
// Web API — nhận JSON
[HttpPost]
public IActionResult Create([FromBody] CreateUserDto dto) { }

// MVC form submit + file
[HttpPost]
public IActionResult Upload([FromForm] IFormFile file, [FromForm] string desc) { }

41. ⭐🔵 Async action method vs sync — khi nào dùng?

// ❌ Sync — block thread khi có I/O
[HttpGet("{id}")]
public IActionResult Get(int id) {
    var u = _db.Users.Find(id);    // block!
    return Ok(u);
}

// ✅ Async — giải phóng thread khi chờ DB/HTTP
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> Get(int id) {
    var u = await _db.Users.FindAsync(id);
    return u is null ? NotFound() : Ok(u);
}

ASP.NET Core thread pool có giới hạn. Với 100 concurrent request, sync + chờ DB 200ms = 100 threads bị block → depleted. Async → chỉ khi DB trả về mới chiếm thread.

42. 🔵 Làm sao trả về file từ API?

// PDF/Excel file
[HttpGet("{id}/invoice")]
public async Task<IActionResult> GetInvoice(int id) {
    var bytes = await _service.GenerateInvoicePdfAsync(id);
    return File(bytes, "application/pdf", $"invoice-{id}.pdf");
}

// Stream (tốt hơn cho file lớn)
[HttpGet("{id}/report")]
public async Task<IActionResult> GetReport(int id) {
    var stream = await _service.GetReportStreamAsync(id);
    return File(stream, "application/octet-stream", "report.xlsx");
}

// Redirect to blob storage
[HttpGet("{id}/image")]
public IActionResult GetImage(int id) {
    var url = _storage.GetSignedUrl($"images/{id}.jpg");
    return Redirect(url);
}

43. 🟡 Model Binding với custom complex type?

// Custom model binder
public class DateRangeModelBinder : IModelBinder {
    public Task BindModelAsync(ModelBindingContext ctx) {
        var from = ctx.ValueProvider.GetValue("from").FirstValue;
        var to = ctx.ValueProvider.GetValue("to").FirstValue;
        if (DateTime.TryParse(from, out var f) && DateTime.TryParse(to, out var t)) {
            ctx.Result = ModelBindingResult.Success(new DateRange(f, t));
        }
        return Task.CompletedTask;
    }
}

// Register
[ModelBinder(typeof(DateRangeModelBinder))]
public class DateRange { ... }

// Action
[HttpGet("report")]
public IActionResult Report(DateRange range) { }  // ?from=2026-01-01&to=2026-12-31

44. ⭐🔵 Paging, Sorting, Filtering pattern chuẩn?

public class QueryParams {
    public int Page { get; set; } = 1;
    public int Size { get; set; } = 20;
    public string? SortBy { get; set; }
    public bool SortDesc { get; set; }
    public string? Search { get; set; }
}

[HttpGet]
public async Task<ActionResult<PagedResult<UserDto>>> GetAll([FromQuery] QueryParams q) {
    var query = _db.Users.AsNoTracking();
    if (!string.IsNullOrEmpty(q.Search))
        query = query.Where(u => u.Name.Contains(q.Search));
    var total = await query.CountAsync();
    var items = await query
        .OrderBy(u => u.Name)
        .Skip((q.Page - 1) * q.Size)
        .Take(q.Size)
        .Select(u => new UserDto { ... })
        .ToListAsync();
    return Ok(new PagedResult<UserDto>(items, total, q.Page, q.Size));
}

45. ⭐🟡 Global exception handling pattern chuẩn?

// Middleware approach (recommend)
public class GlobalExceptionMiddleware {
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;
    
    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger) {
        _next = next; _logger = logger;
    }
    
    public async Task InvokeAsync(HttpContext ctx) {
        try {
            await _next(ctx);
        }
        catch (NotFoundException ex) {
            ctx.Response.StatusCode = 404;
            await ctx.Response.WriteAsJsonAsync(new { error = ex.Message });
        }
        catch (ValidationException ex) {
            ctx.Response.StatusCode = 422;
            await ctx.Response.WriteAsJsonAsync(new { errors = ex.Errors });
        }
        catch (Exception ex) {
            _logger.LogError(ex, "Unhandled exception");
            ctx.Response.StatusCode = 500;
            await ctx.Response.WriteAsJsonAsync(new { error = "Internal server error" });
        }
    }
}

M. So sánh và khác biệt version

46. ⭐ .NET 5 → 6 → 7 → 8 → 9 — thay đổi đáng chú ý?

VersionĐiểm nổi bật
.NET 5 (2020)Hợp nhất .NET Framework và Core thành .NET
.NET 6 LTS (2021)Minimal API, Hot Reload, DateOnly/TimeOnly, file-scoped namespace
.NET 7 (2022)Rate Limiting built-in, Output Caching, Route Groups, Problem Details
.NET 8 LTS (2023)Blazor fullstack, Keyed DI, TimeProvider, frozen collections
.NET 9 (2024)HybridCache, OpenAPI built-in (no Swashbuckle), LINQ CountBy/AggregateBy, Span<T> LINQ
.NET 10 LTS (2025)Better trimming, C# 14, EF Core 10, OpenAPI 3.1

47. 🔵 Startup.cs (cũ) vs Program.cs (mới .NET 6+)?

// Cũ — .NET 5 trở về
// Startup.cs
public class Startup {
    public void ConfigureServices(IServiceCollection services) {
        services.AddControllers();
    }
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
        app.UseRouting();
        app.UseEndpoints(endpoints => endpoints.MapControllers());
    }
}

// Mới — .NET 6+, minimal hosting
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();

Không còn Startup.cs. WebApplicationBuilder kết hợp cả IHostBuilder + IWebHostBuilder + IServiceCollection.

48. 🔵 Keyed DI Services (.NET 8)?

// Đăng ký nhiều implementation cùng interface, phân biệt bằng key
builder.Services.AddKeyedSingleton<IPaymentGateway, StripeGateway>("stripe");
builder.Services.AddKeyedSingleton<IPaymentGateway, VnPayGateway>("vnpay");

// Inject
public class CheckoutService {
    public CheckoutService([FromKeyedServices("stripe")] IPaymentGateway gateway) { }
}

// Hoặc resolve runtime
var gateway = app.Services.GetRequiredKeyedService<IPaymentGateway>("vnpay");

49. 🔵 IExceptionHandler (NET 8 thay vì middleware custom)?

public class GlobalExceptionHandler : IExceptionHandler {
    public async ValueTask<bool> TryHandleAsync(
        HttpContext ctx, Exception ex, CancellationToken ct) {
        var problem = new ProblemDetails {
            Status = ex is NotFoundException ? 404 : 500,
            Title = ex.Message,
            Instance = ctx.Request.Path
        };
        ctx.Response.StatusCode = problem.Status!.Value;
        await ctx.Response.WriteAsJsonAsync(problem, ct);
        return true;        // true = đã handle, false = next handler
    }
}

// Register
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
app.UseExceptionHandler();

N. Câu hỏi thực tế hay hỏi

50. ⭐ Bạn debug performance issue trên ASP.NET Core API ra sao?

  1. Đo trước tiên: dùng dotnet-trace, Application Insights hoặc MiniProfiler. Đừng đoán.
  2. Check DB: LINQ generated SQL có N+1 không? Missing index?
  3. Check async: có Task.Result / .Wait() chặn thread không?
  4. Check allocations: dùng dotnet-counters, JetBrains dotMemory.
  5. Output Caching: có thể cache response của endpoint không?
  6. Response size: có đang trả SELECT * về cả triệu field?

51. ⭐ Token hết hạn trong lúc user đang dùng — xử lý ra sao?

Refresh token pattern:

  1. JWT access token — thời gian ngắn (15m–1h).
  2. Refresh token — lưu DB, thời gian dài (7–30 ngày).
  3. Client detect 401 → tự call /auth/refresh với refresh token.
  4. Server verify refresh token còn hợp lệ → cấp JWT mới.
  5. Nếu refresh token expired → force logout.

52. ⭐ Làm sao test controller mà không cần chạy full server?

// Unit test controller trực tiếp — mock service
public class UsersControllerTests {
    [Fact]
    public async Task GetById_Returns404_WhenNotFound() {
        var mockService = new Mock<IUserService>();
        mockService.Setup(s => s.GetByIdAsync(999)).ReturnsAsync((UserDto?)null);
        
        var controller = new UsersController(mockService.Object);
        var result = await controller.Get(999);
        
        Assert.IsType<NotFoundResult>(result.Result);
    }
}

// Integration test — WebApplicationFactory
public class ApiTests : IClassFixture<WebApplicationFactory<Program>> {
    private readonly HttpClient _client;
    public ApiTests(WebApplicationFactory<Program> factory) {
        _client = factory.CreateClient();
    }
    
    [Fact]
    public async Task GetUsers_Returns200() {
        var response = await _client.GetAsync("/api/users");
        response.EnsureSuccessStatusCode();
    }
}

Tham khảo

© 2026 .NET Fresher Guide. All rights reserved.

Về Trang Web

Hướng dẫn toàn diện để chuẩn bị phỏng vấn vị trí .NET Fresher với nội dung từ lý thuyết đến thực hành.