---
title: Controllers và Actions
description: Giới thiệu chi tiết về Controllers và Actions trong ASP.NET Core MVC — cách định nghĩa, trả về kết quả và xử lý cross-cutting concerns.
---

## Controller là gì?

**Controller** được dùng để định nghĩa và nhóm một tập hợp các **actions**. Một **action** (hay **action method**) là method trên controller dùng để xử lý requests. Controllers nhóm các actions tương tự lại với nhau, cho phép áp dụng các quy tắc chung như **routing**, **caching** và **authorization** cho toàn bộ nhóm.

Requests được map đến actions thông qua **routing**. Controllers được khởi tạo và hủy trên **mỗi request**.

---

## Quy ước đặt tên Controller

Theo quy ước, controller classes:

- Nằm trong thư mục `Controllers` ở root của project
- Kế thừa từ `Microsoft.AspNetCore.Mvc.Controller`

Một class được coi là controller khi thỏa **ít nhất một** trong các điều kiện:

| Điều kiện                              | Ví dụ                    |
| -------------------------------------- | ------------------------ |
| Tên class có suffix `"Controller"`     | `ProductsController`     |
| Kế thừa class có suffix `"Controller"` | Kế thừa `BaseController` |
| Class được decorate với `[Controller]` | `@[Controller]`          |

```csharp
// ✅ Hợp lệ — tên có suffix Controller
public class ProductsController : Controller { }

// ✅ Hợp lệ — có attribute [Controller]
[Controller]
public class Products
{
    public IActionResult Index() => View();
}

// ✅ Hợp lệ — kế thừa class có suffix Controller
public class ProductsController : BaseController { }

// ❌ Không hợp lệ — có [NonController]
[NonController]
public class ProductsController : Controller { }
```

---

## Controller và mô hình MVC

```mermaid
flowchart LR
    A["👤 Request"] --> B["🎮 Controller<br/><small>Initial processing<br/>Request validation</small>"]
    B --> C["📦 Model<br/><small>Business Logic<br/>Data Processing</small>"]
    C --> B2["🎮 Controller"]
    B2 --> D["🖼️ View hoặc API Response"]
```

Trong mô hình MVC, controller chịu trách nhiệm:

1. **Xử lý request ban đầu** — đảm bảo request data hợp lệ
2. **Instantiate model** — tạo đối tượng model
3. **Chọn kết quả trả về** — View hoặc API response

> **Lưu ý:** Controller là một abstraction ở **mức UI**. Trong ứng dụng được thiết kế tốt, controller **không trực tiếp** thực hiện data access hay business logic. Thay vào đó, controller **delegates** đến các services xử lý các trách nhiệm này.

---

## Dependency Injection trong Controller

Controllers nên tuân theo **Explicit Dependencies Principle**.

| Loại injection            | Khi nào dùng                          |
| ------------------------- | ------------------------------------- |
| **Constructor Injection** | Nhiều action methods cần cùng service |
| **Action Injection**      | Chỉ một action method cần service đó  |

### Constructor Injection

```csharp
public class ProductsController : Controller
{
    private readonly IProductService _productService;
    private readonly ICategoryService _categoryService;

    public ProductsController(
        IProductService productService,
        ICategoryService categoryService)
    {
        _productService = productService;
        _categoryService = categoryService;
    }
}
```

### Action Injection

```csharp
public class ProductsController : Controller
{
    [FromServices]
    public IProductService ProductService { get; set; }

    public IActionResult Details(int id)
    {
        var product = ProductService.GetById(id);
        return View(product);
    }
}
```

---

## Action Methods

### Định nghĩa Action

Tất cả **public methods** trên controller đều là actions, **trừ** method có attribute `[NonAction]`.

```csharp
public class ProductsController : Controller
{
    // ✅ Đây là action
    public IActionResult Index() => View();

    // ✅ Đây là action (async)
    public async Task<IActionResult> Details(int id)
    {
        var product = await _productService.GetByIdAsync(id);
        return View(product);
    }

    // ❌ Đây không phải action
    [NonAction]
    public string GetProductCode(int id)
    {
        return $"PRD-{id}";
    }
}
```

### Action Parameters và Model Binding

Parameters trên actions được bind từ request data và validated bằng **model binding**. **Model validation** xảy ra cho tất cả model-bound parameters. Property `ModelState.IsValid` cho biết binding và validation có thành công không.

```csharp
public async Task<IActionResult> Create(Product product)
{
    if (!ModelState.IsValid)  // ← Validation check
    {
        return View(product);
    }

    await _productService.CreateAsync(product);
    return RedirectToAction(nameof(Index));
}
```

### Action Results

Actions có thể trả về bất kỳ type nào, nhưng thường trả về `IActionResult` (hoặc `Task<IActionResult>` cho async methods).

```mermaid
flowchart TD
    A["IActionResult"] --> B["Ko có Response Body"]
    A --> C["Có Response Body"]
    B --> B1["HTTP Status Code<br/>BadRequest, NotFound, Ok"]
    B --> B2["Redirect<br/>RedirectToAction, RedirectToRoute"]
    C --> C1["View<br/>View(model)"]
    C --> C2["Formatted Response<br/>JSON, File, PhysicalFile"]
    C --> C3["Content Negotiation<br/>Ok(value), CreatedAtRoute"]
```

---

## Controller Helper Methods

Controllers kế thừa từ `Controller` có quyền truy cập 3 nhóm helper methods:

### 1. Trả về Response Body rỗng

Không có `Content-Type` HTTP header vì response body không có nội dung.

#### HTTP Status Code

```csharp
public IActionResult GetProduct(int id)
{
    var product = _productService.GetById(id);

    if (product == null)
        return NotFound();        // → 404

    return Ok(product);           // → 200
}
```

| Method            | HTTP Status | Mô tả                 |
| ----------------- | ----------- | --------------------- |
| `BadRequest()`    | 400         | Bad request           |
| `NotFound()`      | 404         | Không tìm thấy        |
| `Ok()`            | 200         | Thành công            |
| `NoContent()`     | 204         | Không có nội dung     |
| `StatusCode(500)` | 500         | Internal server error |

#### Redirect

```csharp
public IActionResult Create(Product product)
{
    if (!ModelState.IsValid)
        return View(product);

    _productService.Create(product);
    return RedirectToAction("Index");  // → 302 + Location header

    // Hoặc
    return RedirectToAction("Details", new { id = product.Id });
}
```

| Method                     | Mô tả                                             |
| -------------------------- | ------------------------------------------------- |
| `Redirect(url)`            | Redirect đến URL                                  |
| `LocalRedirect(url)`       | Redirect đến local URL, ngăn open redirect attack |
| `RedirectToAction(action)` | Redirect đến action                               |
| `RedirectToRoute(route)`   | Redirect đến route cụ thể                         |

---

### 2. Trả về Response Body có predefined Content-Type

#### View

```csharp
public IActionResult Index()
{
    var products = _productService.GetAll();
    return View(products);  // → Render .cshtml file
}
```

#### Formatted Response

```csharp
public IActionResult GetJson()
{
    var product = _productService.GetById(1);
    return Json(product);  // → Content-Type: application/json
}
```

| Method                     | Content-Type       | Mô tả                          |
| -------------------------- | ------------------ | ------------------------------ |
| `Json(object)`             | `application/json` | Serialize object sang JSON     |
| `File(path, type)`         | Tùy chỉnh          | Trả về file                    |
| `PhysicalFile(path, type)` | Tùy chỉnh          | Trả về file từ filesystem      |
| `Content(text, type)`      | Tùy chỉnh          | Trả về string với content type |

```csharp
// Trả về XML file
return File(fileBytes, "application/xml", "products.xml");

// Trả về file download
return PhysicalFile(path, "application/octet-stream", "report.pdf");
```

---

### 3. Content Negotiation

**Content Negotiation** xảy ra khi action trả về `ObjectResult` hoặc non-`IActionResult`. Các method này tự động chọn format (JSON/XML) dựa trên `Accept` header của client.

```csharp
public IActionResult GetProduct(int id)
{
    var product = _productService.GetById(id);
    if (product == null)
        return BadRequest(ModelState);         // Content negotiation ✅
        // return BadRequest();                // Không có content negotiation

    return Ok(product);                       // Content negotiation ✅
    // return Ok();                           // Không có content negotiation
}

// Luôn luôn content negotiation
return CreatedAtRoute("GetProduct", new { id = product.Id }, product);
```

---

## Cross-Cutting Concerns

**Cross-cutting concerns** là các phần workflow được chia sẻ giữa nhiều parts của ứng dụng, như authentication, logging, error handling.

```mermaid
flowchart LR
    A["Request"] --> F1["🛡️ Authorization Filter"]
    F1 --> F2["⚡ Action Filter"]
    F2 --> F3["📦 Controller Action"]
    F3 --> F4["❗ Exception Filter"]
    F4 --> F5["🔄 Result Filter"]
    F5 --> G["Response"]
```

### Ví dụ: Cross-cutting concerns thường gặp

```csharp
[Authorize]                    // 🛡️ Chỉ user đã auth mới truy cập
[ResponseCache(Duration = 30)] // 💾 Cache response 30 giây
public class CartController : Controller
{
    [AllowAnonymous]           // ⚠️ Override [Authorize] cho action này
    public IActionResult Index()
    {
        return View();
    }
}
```

| Cross-cutting concern | Giải pháp                               |
| --------------------- | --------------------------------------- |
| **Authentication**    | `[Authorize]`, `[AllowAnonymous]`       |
| **Authorization**     | Policy-based authorization              |
| **Error Handling**    | `[ExceptionHandler]` filter, middleware |
| **Response Caching**  | `[ResponseCache]` filter                |
| **Logging**           | Action filter hoặc middleware           |
| **Localization**      | Action filter                           |

---

## Tài liệu tham khảo

- [Overview of ASP.NET Core MVC](https://learn.microsoft.com/en-us/aspnet/core/mvc/overview)
- [Get started with ASP.NET Core MVC](https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/start-mvc)
- [Dependency Injection into Controllers](https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection)
- [Filters](https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters)
