Best Practices for ASP.NET MVC: Controller (Phần 3)


[Bài viết này dựa trên một tài liệu của tác giả Ben Grover (một nhà phát triển cấp cao từ Microsoft). Chúng tôi dự định sẽ đưa những thông tin này vào phần tài liệu MVC 3 trên trang MSDN. Chúng tôi hi vọng được nghe những phản hổi  và mong chờ bất kỳ góp ý nào từ phía các bạn]

Bài viết này giới thiệu một tập các hướng dẫn lập trình nhắm đến các lập trình viên ASP.NET MVC. Tất nhiên, bạn, với tư cách là nhà phát triển sẽ vẫn là người quyết định cuối cùng trong việc chọn hướng dẫn nào phù hợp nhất.

Các khuyến nghị trong việc tạo Controller

Controller (với một action method cụ thể) được gọi bởi hệ thống định tuyến (routing system) bằng cách dựa trên dạng của URL. Controller nhận vào input từ routing system, trong đó bao gồm cả các thành phần trong HTTP request context (session, cookies, browser, …).

Nên: dùng model binding thay vì tự xử lý request.

ASP.NET trừu tượng hóa nhiều lệnh liên quan đến việc tạo lại đối tượng bằng cách dùng model binding. Model binding là một kỹ thuật trong đó dữ liệu từ request sẽ được tự động trích xuất để tạo nên kiểu dữ liệu đầu vào cho phương thức action.

Ví dụ sau cho thấy một lớp Seller định nghĩa dữ liệu có thể dùng để gửi từ một form đăng ký người bán (seller):

public class Seller
{
    public Int64 ID { get; set; }
    public string Name { get; set; }
    public string Phone { get; set; }
    public string Address { get; set; }
}

Form đăng ký sẽ được chứa trong view Register và có dạng sau:

<% using (Html.BeginForm()) { %>
  <legend>Account Information</legend>
  <p>
      <%= Html.TextBox("Name") %>
  </p>
  <p>
      <%= Html.TextBox("Phone") %>
  </p>
  <p>
      <%= Html.TextBox("Address") %>
  </p>
  <p>
      <input value="Register" type="submit" />
  </p>
<% } %>

Controller có thể đăng ký một phương thức action để cung cấp model binding như dưới đây:

public ActionResult Register([Bind(Exclude="ID")] Seller newSeller)
{
   ...
}

Default model binder sẽ dò từng thuộc tính trong lớp cho trước theo thứ tự sau (lấy ví dụ thuộc tính Name):

  1. Request.Form["Name"], nếu có
  2. RouteData.Values["Name"], nếu có
  3. Request.QueryString["Namel"], nếu có
  4. Ngược lại, null

Như bạn có thể thấy từ phương thức action Register, có một số attribute bạn có thể đặt cho đối tượng tham số sẽ được gọi ởi model binder mặc nhiên.

Hệ thống model binding cũng sẽ chạy các phép kiểm tra được áp dụng cho đối tượng dữ liệu, kiểu như các annotation attibute (Xem thêm “Nên: đặt tất cả các quy tắc xác định giá trị hợp lệ của dữ liệu bên trong model.“).

Hệ thống model binding có rất nhiều cơ chế mở rộng cho phép chúng ta tùy biến tất cả các thao tác tạo, đưa dữ liệu vào đối tượng hay kiểm tra dữ liệu của nó.

Nên: Chỉ ra tên view cụ thể trong phương thức action.

Sau khi bạn đã thiết lập đầy đủ các giá trị để tạo mã HTML trong phương thức action, bạn sẽ trả về ViewResult hoặc PartialViewResult. Nếu bạn không truyền tên view, tên file view sẽ được chọn tự động dựa trên tên phương thức action. Ví dụ, cho một controller có tên Products với một phương thức action có tên List. Bạn có thể gọi “return View()” từ bên trong phương thức List mà không cần bất kỳ tham số nào. MVC Framework sẽ tìm kiếm một view có tên /Views/Products/List.aspx. Nếu không tìm thấy, sẽ tìm tiếp /Views/Products/List.ascx. Nếu vẫn không tìm thấy, nó sẽ thử tìm /Views/Shared/List.aspx và rồi /Views/Shared/List.ascx. Do vậy, bạn có thể dùng /Views/Shared cho các view có thể được dùng bởi nhiều controller khác nhau.

Để tránh tranh chấp, tốt nhất bạn nên chỉ rõ tên view, kiểu như “return View(“một tên cụ thể nào đó”)”, trong phương thức action. Điều này cho phép bạn gọi List từ các action khác nhau, và framework sẽ không phải đi tìm các view khác nhau.

Chỉ rõ Post/Redirect/Get (PRG) khi gửi form.

Căn cứ theo định nghĩa của HTTP POST và GET:

  • HTTP GET được dùng cho dữ liệu không dùng để thay đổi mô hình dư liệu.
  • HTTP POST được dùng cho dữ liệu để thay đổi mô hình dư liệu.

Cho là các định nghĩa này đã phân định rõ ràng, khi nhận dữ liệu từ form trong phương thức action, bạn có thể trả về RedirectToAction(<actionName>), điều này sẽ tạo ra một mã HTTP 302 (temporary redirect) và tạo lại một lời gọi GET đến <actionName>. Đây chính là mẫu thiết kế Post-Redirect-Get.

Do vậy, đừng dùng HTTP GET để gửi dữ liệu trong form, vì nó vi phạm mục đích của GET.

Ví dụ, trong lược đồ dưới đây, với standard post back, bạn dùng GET và POST trên cùng một URL (create.aspx). Đây sẽ là vấn đề nếu người dùng không chịu chờ cho đến khi việc post dữ liệu hoàn thành. Nếu họ nhấn nút refresh, sẽ có thể xảy ra việc dữ liệu bị gửi lên thêm một lần nữa. Bạn có thể giải quyết vấn đề này trong MVC bằng cách dùng mẫu Post-Redirect-Get.

PRG_pattern

Tuy nhiên, phương pháp này có thể làm giảm hiệu năng phía client, vì lệnh redirect sẽ tạo ra thêm request về phía server, điều này phải được đem ra cân nhắc với tính khả dụng khi quyết định chọn lựa mô hình.

Nên: xử lý HandleUnknowAction và HandleError.

Giá trị trả về mặc nhiên cho một thao tác không xác định được là 404 (Not Found). Nếu bạn override lại HandleUnknownAction trong một controller, bạn có thể đưa ra view mặc nhiên mỗi khi xảy ra lỗi này (404). Thêm nữa, nên đặt thuộc tính HandleError trên một action hay một controller và bạn có thể cung cấp một trang thông báo lỗi chuẩn nếu có exception xảy ra.

One thought on “Best Practices for ASP.NET MVC: Controller (Phần 3)

  1. Pingback: Best Practices for ASP.NET MVC: View (Phần 2) « Đào Hải Nam

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s