Cách định tuyến trong ASP.NET Core MVC

Tôi đã thiết kế bài viết này cho những người mới bắt đầu lập trình web bằng ASP.NET Core và muốn tìm hiểu các bước cơ bản cần thiết để thêm một endpoint (điểm cuối) mới vào ứng dụng ASP.NET Core MVC hiện có.

Trong bài viết này, tôi sẽ giải thích mục đích của từng bước cho đến khi chúng ta có một endpoint mới. Các nhà phát triển cũng có thể sử dụng hướng dẫn này để chẩn đoán các vấn đề trong đó endpoint không thể truy cập được và họ không chắc tại sao.

Lưu ý: Bài viết này giả sử bạn đã bắt đầu với mẫu ASP.NET Core Web App cho MVC.

Bước 1. Project SDK

Đã có rất nhiều thay đổi trong quá trình phát triển .NET, đặc biệt là cách chúng ta thêm các thành phần phụ thuộc vào dự án của mình. Bắt đầu với mẫu ASP.NET Core Web App cho MVC, chúng ta sẽ nhận được một ứng dụng web có thể chạy được ngay. Tuy nhiên, đôi khi chúng ta cũng có thể bắt đầu từ một ứng dụng console hoặc thư viện lớp mà chúng ta muốn trở thành một ứng dụng web ASP.NET Core.

Để đảm bảo rằng dự án của chúng ta đã sẵn sàng trở thành một ứng dụng web, chúng ta cần thêm SDK thích hợp. Để làm điều đó, bên trong tệp dự án .csproj, chúng ta cần thiết lập giá trị thích hợp cho thuộc tính Sdk trên phần tử Project của chúng ta thành Microsoft.NET.Sdk.Web.

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
    </PropertyGroup>

</Project>

Các vấn đề thiếu kiểu dữ liệu và namespace trong một dự án có thể liên quan đến lỗi cụ thể này.

Bước 2. Trong Startup.cs

ASP.NET Core MVC là một cách tiếp cận dựa trên quy ước để xây dựng các ứng dụng web. Mặc dù có những quy ước mặc định đi kèm với framework, nhưng chúng ta có thể thay đổi chúng để phù hợp với nhu cầu của mình.

Đầu tiên, vì tất cả các controller đều được xây dựng bởi bộ định vị dịch vụ (service locator) trong ASP.NET Core, chúng ta cần phải có công cụ quét qua dự án của chúng ta và đăng ký tất cả các Controller. Đăng ký controller được thực hiện trong phương thức ConfigureServices trong lớp Startup của chúng ta.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
}

Tiếp theo, chúng ta sẽ muốn thay đổi cách định tuyến. Theo mặc định, đã có một mẫu định tuyến đã được tạo sẵn.

endpoints.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Mặc dù điều này hiệu quả, nhưng tôi không khuyên bạn nên sử dụng nó vì tính đơn giản của định tuyến này trở thành một trách nhiệm khi một ứng dụng web ngày càng phức tạp. Chúng ta sẽ cần thay đổi dòng này thành dòng sau.

endpoints.MapControllers();

Hãy bắt đầu tạo một định tuyến mới trong ứng dụng web của chúng ta!

Bước 3. Định tuyến trong controller

Chúng ta đã loại bỏ định tuyến mặc định để có lợi cho các tuyến rõ ràng được xác định trong các lớp Controller của chúng ta. Controller là một lớp cụ thể thường kế thừa từ lớp Controller và có ít nhất một phương thức public ánh xạ tới các public endpoint. Hầu hết các ứng dụng bắt đầu sẽ có sẵn một controller tên là HomeController.

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public IActionResult Privacy()
    {
        return View();
    }

    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error()
    {
        return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
    }
}

Hãy bắt đầu bằng cách tạo các định tuyến rõ ràng cho các phương thức Index, Privacy Error của HomeController. Ở đây chúng ta sẽ sử dụng hai thuộc tính trên mỗi phương thức. Đầu tiên, chúng ta sẽ sử dụng thuộc tính Route cho mỗi mỗi phương thức public của HomeController.

public class HomeController : Controller
{
    [Route("")]
    public IActionResult Index()
    {
        return View();
    }

    [Route("privacy")]
    public IActionResult Privacy()
    {
        return View();
    }

    [Route("error")]
    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error()
    {
        return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
    }
}

Chúng ta có thể dừng lại ở đây, nhưng để rõ ràng hơn, chúng ta cũng sẽ thêm các thuộc tính để giới hạn các loại yêu cầu có thể truy cập các endpoint (điểm cuối) này. Tất cả các điểm cuối này chỉ có thể truy cập thông qua các yêu cầu GET, vì vậy chúng ta sẽ sử dụng thuộc tính HttpGet để giới hạn nó trong phương thức đó.

public class HomeController : Controller
{
    [HttpGet, Route("")]
    public IActionResult Index()
    {
        return View();
    }

    [HttpGet, Route("privacy")]
    public IActionResult Privacy()
    {
        return View();
    }

    [HttpGet, Route("error")]
    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error()
    {
        return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
    }
}

Thật tuyệt vời! Hãy tạo một điểm cuối hoàn toàn mới chấp nhận thông tin từ view Index của chúng ta thông qua HTML Form.

Bước 4. Tạo một điểm cuối mới

Vì chúng ta đang xây dựng một ứng dụng web, chúng ta sẽ muốn người dùng tương tác với giao diện người dùng của chúng ta. Hãy bắt đầu bằng cách tạo một class hoàn toàn mới đại diện cho dữ liệu mà chúng ta muốn từ người dùng. Trong trường hợp này, chúng ta muốn tên của người bạn thân nhất của họ.

Trong thư mục Models, chúng ta có thể tạo một tệp mới có tên BestFriend.cs với nội dung sau:

public record BestFriend(string Name);

Tiếp theo, trong HomeController chúng ta sẽ thêm một điểm cuối mới bằng cách sử dụng các cấu trúc tương tự. Điểm cuối mới sẽ sử dụng phương thức HttpPost thay vì sử dụng phương thức HttpGet như các điểm cuối trước đây. Chúng ta cũng sẽ cập nhật phương thức Index để nhận thông tin tên của người bạn thân nhất mà người dùng nhập vào để hiển.

[HttpGet, Route("")]
public IActionResult Index()
{
    var model = new BestFriend(TempData["BestFriend"] as string);
    return View(model);
}

[HttpPost, Route("")]
public IActionResult Index([FromForm] BestFriend friend)
{
    // storing the value
    TempData["BestFriend"] = friend.Name;
    return RedirectToAction("Index");
}

ASP.NET Core yêu cầu chúng ta chỉ định nguồn data binding sẽ sử dụng để điền các đối số phương thức của chúng tôi. Trong trường hợp này, đối số friend sẽ đến từ một biểu mẫu được gửi từ một trang HTML. Chúng tôi sử dụng thuộc tính FromForm trên đối số mà chúng ta muốn phổ biến.

Hãy xem cách chúng ta thiết lập điều đó.

Bước 5. Cập nhật view

Ở bước 3, chúng ta muốn truyền model BestFriend tới view Index. Chúng ta có thể làm điều này bằng cách sử dụng chỉ thị @model ở đầu của view Index. Trong tệp Index.cshtml đặt tại đường dẫn Views/Home, chúng ta sẽ thêm phần sau vào đầu tệp.

@model BestFriend

Tiếp theo, chúng ta sẽ cần tạo một form HTML với phương thức POST trỏ tới điểm cuối mới của chúng ta mới tạo ở bước 3. Hãy sửa view của chúng ta để thêm form với một ô nhập văn bản (textbox) và nút Submit. Sau khi người dùng nhập thông tin vào textbox và nhấn nút Submit, dữ liệu sẽ được gửi đến điểm cuối POST của chúng ta.

@model BestFriend
@{
    ViewData["Title"] = "Home Page";
}

@if (!string.IsNullOrWhiteSpace(Model?.Name))
{
    <h1>My Best Friend is @Model.Name!</h1>
}

<form asp-action="Index" method="post">
    <label asp-for="Name"></label>
    <input type="text" asp-for="Name" />
    <button type="submit">Submit</button>
</form>

Bây giờ chúng ta sẽ có thể chạy ứng dụng của mình, nhập tên của một người bạn thân và gửi nó đến điểm cuối mới của chúng ta.

Kết luận và Checklist

Khi thêm một điểm cuối mới, chúng ta nên thực hiện các bước sau:

  1. Kiểm tra để đảm bảo rằng chúng ta đã đăng ký controller và các tuyến vào ứng dụng ASP.NET Core của chúng ta.
  2. Kế thừa từ lớp cơ sở Controller thích hợp. Tuy không cần thiết nhưng nó khiến mọi thứ trở nên đơn giản hơn rất nhiều.
  3. Thêm vào các điểm cuối của chúng ta bằng các thuộc tính thích hợp như Route HttpGet, HttpPost, v.v
  4. Đảm bảo rằng view của chúng ta đang sử dụng các model thích hợp.
  5. Form HTML của chúng ta (hoặc ứng dụng khách) đang gửi lại thông tin thích hợp trong một giao thức mà chúng ta mong đợi. Trong trường hợp của bài viết này, chúng ta đã sử dụng form HTML, nhưng chúng ta cũng có thể sử dụng JSON.
  6. Các điểm cuối chấp nhận nguồn dữ liệu mà chúng ta đã chỉ định vị trí để tìm kiếm các giá trị. Trong ví dụ này, chúng ta đã sử dụng FromForm.

Cảm ơn bạn vì đã dành thời gian đọc bài viết này!

ASP.NET Core MVCASP.NET Core.NET CoreLập Trình C#
Bài Viết Liên Quan:
10 thư viện .NET Core hàng đầu mà mọi nhà phát triển web nên biết
Trung Nguyen 08/04/2021
10 thư viện .NET Core hàng đầu mà mọi nhà phát triển web nên biết

Trong bài viết này, tôi sẽ giới thiệu một số thư viện .NET Core hữu ích nhất mà mọi nhà phát triển cần biết.

Request Life Cycle trong ASP.NET Core
Trung Nguyen 04/03/2021
Request Life Cycle trong ASP.NET Core

Requets life cycle trong ASP.NET Core MVC là một chuỗi các thành phần tương tác với nhau để xử lý HTTP request và trả về response cho client.

ASP.NET Core vs Go: Hiệu suất HTTP của ai tốt hơn
Trung Nguyen 11/10/2020
ASP.NET Core vs Go: Hiệu suất HTTP của ai tốt hơn

Trong bài viết này, chúng tôi sẽ so sánh hiệu suất HTTP của ASP.NET Core và Go sử dụng mô hình kiến trúc MVC.

ASP.NET Core Dependency Injection: Best Practice, Mẹo và Thủ Thuật
Trung Nguyen 10/10/2020
ASP.NET Core Dependency Injection: Best Practice, Mẹo và Thủ Thuật

Trong bài viết này, tôi sẽ chia sẻ các best practice, mẹo và thủ thuật về việc sử dụng Dependency Injection (DI) trong ứng dụng ASP.NET Core.