Views là gì?

Trong mô hình Model-View-Controller (MVC), View xử lý trình bày dữ liệutương tác người dùng của ứng dụng. View là một HTML template với Razor markup nhúng vào. Razor markup là code tương tác với HTML markup để tạo webpage được gửi đến client. Trong ASP.NET Core MVC, views là các file .cshtml sử dụng ngôn ngữ lập trình C# trong Razor markup.

Cấu trúc thư mục Views

Thông thường, các view files được nhóm vào các folders có tên theo từng controller của ứng dụng. Các folders này được lưu trong thư mục Views ở root của ứng dụng:
MyApp/
├── Views/
│   ├── Home/            ← Views cho HomeController
│   │   ├── Index.cshtml
│   │   ├── About.cshtml
│   │   └── Contact.cshtml
│   ├── Products/        ← Views cho ProductsController
│   │   ├── Index.cshtml
│   │   ├── Details.cshtml
│   │   └── Create.cshtml
│   └── Shared/          ← Views dùng chung
│       ├── _Layout.cshtml
│       └── _PartialView.cshtml
Controller Home được đại diện bởi thư mục Home trong Views. Thư mục này chứa views cho About, Contact và Index. Khi người dùng request một trong 3 webpages này, các controller actions trong Home controller quyết định view nào được dùng để build và trả về webpage.

Lợi ích của Views

Views giúpEstablish separation of concerns trong ứng dụng MVC bằng cách tách markup giao diện người dùng khỏi các phần khác của ứng dụng. Việc tuân theo SoC design giúp ứng dụng modular, mang lại nhiều lợi ích:
Lợi íchMô tả
Dễ bảo trìỨng dụng được tổ chức tốt hơn. Views được nhóm theo tính năng, dễ tìm khi làm việc trên một tính năng
Loose couplingCó thể build và update views riêng biệt với business logic và data access components
Dễ testCác phần giao diện người dùng dễ test vì views là các đơn vị riêng biệt
Giảm trùng lặpÍt khả năng vô tình lặp lại các phần giao diện

Layout

Sử dụng Layouts để cung cấp các phần webpage nhất quán và giảm code trùng lặp. Layouts thường chứa header, navigation, menu elements và footer. Header và footer thường chứa boilerplate markup cho nhiều metadata elements và links đến script và style assets.
<!DOCTYPE html>
<html>
<head>
    <title>@ViewData["Title"] - My App</title>
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav>@await Html.PartialAsync("_Menu")</nav>
    </header>

    <div class="container">
        @RenderBody()  @* Nội dung page được render ở đây *@
    </div>

    <footer>
        &copy; 2025 My App
    </footer>

    <script src="~/js/site.js"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Mỗi view sử dụng layout bằng directive @model ở đầu file:
@model MyViewModel
@{
    Layout = "_Layout";
}

<h2>@Model.Title</h2>
<p>@Model.Description</p>

Partial Views

Partial views giảm code trùng lặp bằng cách quản lý các phần có thể tái sử dụng của views. Partial view hữu ích cho phần author biography trên blog xuất hiện ở nhiều views. Author biography là ordinary view content và không cần code thực thi để produce content cho webpage.
@* Partial view: AuthorBio.cshtml *@
<div class="author-bio">
    <img src="@Model.AvatarUrl" alt="@Model.AuthorName" />
    <h4>@Model.AuthorName</h4>
    <p>@Model.Bio</p>
</div>
Render partial view từ một view khác:
@* Trong một view khác *@
<partial name="AuthorBio" model="@Model.Author" />

View Components

View Components tương tự partial views ở chỗ cho phép giảm code trùng lặp, nhưng phù hợp cho view content cần code chạy trên server để render webpage. View Components hữu ích khi rendered content cần database interaction, ví dụ shopping cart trên website.
public class ShoppingCartViewComponent : ViewComponent
{
    private readonly ICartService _cartService;

    public ShoppingCartViewComponent(ICartService cartService)
    {
        _cartService = cartService;
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var items = await _cartService.GetCartItemsAsync();
        return View(items);
    }
}
@* Views/Shared/Components/ShoppingCart/Default.cshtml *@
@model IEnumerable<CartItem>

<div class="shopping-cart">
    <h3>Giỏ hàng của bạn</h3>
    @if (Model.Any())
    {
        <ul>
            @foreach (var item in Model)
            {
                <li>@item.ProductName - @item.Quantity x @item.Price</li>
            }
        </ul>
    }
    else
    {
        <p>Giỏ hàng trống.</p>
    }
</div>
Sử dụng trong layout hoặc view:
@await Component.InvokeAsync("ShoppingCart")

Tạo View

Views cụ thể cho một controller được tạo trong thư mục Views/[ControllerName]. Views dùng chung giữa các controllers được đặt trong Views/Shared. Để tạo view, thêm file mới và đặt tên giống tên action tương ứng với extension .cshtml. Để tạo view tương ứng với action About trong HomeController, tạo file About.cshtml trong Views/Home:
@{
    ViewData["Title"] = "About";
}

<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>

<p>Sử dụng khu vực này để cung cấp thông tin bổ sung.</p>

Razor Markup cơ bản

Razor markup bắt đầu với symbol @. Chạy C# statements bằng cách đặt code C# trong Razor code blocks được set off bằng curly braces:
@{
    var greeting = "Xin chào";
    var userName = "Người dùng";
}

<p>@greeting, @userName!</p>
Hiển thị giá trị trong HTML bằng cách reference với @:
<h2>@ViewData["Title"]</h2>
<p>@Model.Description</p>

Controller trả về View

Views thường được trả về từ actions như ViewResult, là một loại ActionResult. Controller kế thừa từ Controller có thể dùng helper method View():
public IActionResult About()
{
    ViewData["Message"] = "Trang mô tả ứng dụng của bạn.";
    return View();
}

Các overload của View()

// Trả về view cùng tên với action
return View();

// Trả về view có tên cụ thể
return View("Orders");

// Trả về view với model
return View(order);

// Trả về view cụ thể với model
return View("OrderDetails", order);

View Discovery

Khi một action trả về view, một quá trình gọi là view discovery xác định view file nào được sử dụng dựa trên view name. Mặc định, method View() trả về view cùng tên với action method. Runtime tìm view theo thứ tự:

Các cách chỉ định view path

// Absolute path từ app root — phải có extension .cshtml
return View("Views/Home/About.cshtml");

// Relative path
return View("../Manage/Index");

// Relative path với prefix ./ để chỉ controller hiện tại
return View("./About");

Truyền dữ liệu đến Views

Có nhiều cách để truyền dữ liệu đến views:

Strongly Typed Data (ViewModel)

Cách mạnh mẽ nhất là chỉ định model type trong view — gọi là viewmodel. Sử dụng viewmodel cho phép view tận dụng strong type checking và hỗ trợ IntelliSense.

Khai báo model trong view

@model WebApplication1.ViewModels.Address

<h2>Liên hệ</h2>
<address>
    @Model.Street<br>
    @Model.City, @Model.State @Model.PostalCode<br>
    <abbr title="Phone">ĐT:</abbr> 425.555.0100
</address>

Controller truyền model

public IActionResult Contact()
{
    var viewModel = new Address
    {
        Name = "Công ty ABC",
        Street = "123 Đường ABC",
        City = "TP. Hồ Chí Minh",
        State = "HCM",
        PostalCode = "70000"
    };

    return View(viewModel);
}

ViewModel class

namespace WebApplication1.ViewModels
{
    public class Address
    {
        public string Name { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string PostalCode { get; set; }
    }
}
Lưu ý: Nên sử dụng POCO (Plain Old CLR Object) viewmodels — có ít hoặc không có behavior (methods). ViewModels được lưu trong folder Models hoặc folder ViewModels riêng ở root của ứng dụng.

ViewData

ViewDataViewDataDictionary object, truy cập qua string keys. String data có thể được stored và used trực tiếp, nhưng phải cast các object values sang specific types khi extract.

Trong Controller

public IActionResult SomeAction()
{
    ViewData["Greeting"] = "Xin chào";
    ViewData["Address"] = new Address
    {
        Name = "Steve",
        Street = "123 Main St",
        City = "Hudson",
        State = "OH",
        PostalCode = "44236"
    };

    return View();
}

Trong View

@{
    // Vì Address không phải string, cần cast
    var address = ViewData["Address"] as Address;
}

@ViewData["Greeting"] World!

<address>
    @address.Name<br>
    @address.Street<br>
    @address.City, @address.State @address.PostalCode
</address>

[ViewData] Attribute

Properties trên controllers được đánh dấu với attribute [ViewData] sẽ có values được stored và loaded từ dictionary:
public class HomeController : Controller
{
    [ViewData]
    public string Title { get; set; }

    public IActionResult About()
    {
        Title = "Về chúng tôi";
        ViewData["Message"] = "Trang mô tả ứng dụng.";
        return View();
    }
}
@* Trong layout *@
<head>
    <title>@ViewData["Title"] - My App</title>
</head>

ViewBag

ViewBag là wrapper quanh ViewData, cung cấp dynamic properties cho underlying ViewData collection. ViewBag tiện lợi hơn vì không cần cast.

Controller

public IActionResult SomeAction()
{
    ViewBag.Greeting = "Xin chào";
    ViewBag.Address = new Address
    {
        Name = "Steve",
        Street = "123 Main St",
        City = "Hudson",
        State = "OH",
        PostalCode = "44236"
    };

    return View();
}

View

@ViewBag.Greeting World!

<address>
    @ViewBag.Address.Name<br>
    @ViewBag.Address.Street<br>
    @ViewBag.Address.City, @ViewBag.Address.State @ViewBag.Address.PostalCode
</address>

So sánh ViewData vs ViewBag vs ViewModel

Tiêu chíViewDataViewBagViewModel (@model)
Type checkingRuntime (dynamic)Runtime (dynamic)Compile-time ✅
IntelliSense
Cast required✅ Có❌ Không❌ Không
Syntax@ViewData["Key"]@ViewBag.Key@Model.Property
Khuyến nghịHạn chếHạn chếƯu tiên dùng
Khuyến nghị: Nên sử dụng ViewModel (@model) làm cách chính để truyền dữ liệu. ViewData và ViewBag dễ gây lỗi runtime vì không có compile-time checking.

Tài liệu tham khảo