- Code-behind trang Razor Page
- Code trong trang Razor Page
- Áp dụng Code-behind
- Các phương thức Handler PageModel
- Các phương thức Named Handler PageModel
- Truyền dữ liệu PageModel đến Razor với ViewData
- Handler trả về IActionResult
Sử dụng code-behind trang Razor Page
Kỹ thuật code-behind áp dụng cho ứng dụng web kiểu Razor Page đó là sự phân tách giữa mã HTML viết trong Razor Page và mã nguồn C#. Để thực hành tìm hiểu, hãy nhanh chóng tạo ra một dự án ASP.NET Core Razor Page như sau:
Tạo thư mục dự án razor04.codebehide
, vào thư mục này và thực hiện lệnh
dotnet new webapp
Do chưa đến phần áp dụng cơ sở dữ liệu vào trang, nên tạo ra một mô hình dữ liệu đơn giản như sau. Tạo một lớp biểu diễn sản phẩm và một danh sách sản phẩm
using System; using System.Collections.Generic; using System.Linq; namespace razor04.codebehide.Models { // Lớp Product public class Product { public int ID {set; get;} public String Name {set; get;} public String Desciption {set; get;} public Decimal Price {set; get;} = 0; } // Lớp tĩnh giả định DbContext public static class ProductContext { public static List<Product> products; static ProductContext() { // Khởi tạo một danh sách các sản phẩm mẫu products = new List<Product>() { new Product { ID=1, Name = "Iphone", Price = 900, Desciption = "Điện thoại Iphone abc, xyz ..." }, new Product { ID = 2, Name = "Samsung", Price = 800, Desciption = "Điện thoại Samsung, samsung điện thoại ..." }, new Product { ID = 3, Name = "Nokia", Price = 700, Desciption = "Điện thoại Nokia, điện thoại Android" } }; } // Tìm sản phẩm theo ID public static Product FindProductByID(int ID) { var p = from product in products where product.ID == ID select product; return p.FirstOrDefault(); } } } // Lấy danh sách sản phẩm: ProductContext.products // Tìm một sản phẩm theo ID: ProductContext.FindProductByID(id)
Giờ ta sẽ tạo một Razor Page có có tên Pages/ViewProduct.cshtml
để hiện
thị về một sản phẩm theo ID, sử dụng mô hình dữ liệu trên, trường hợp đầu tiên ta sẽ viết code sử dụng dữ liệu
ngay trong .cshtml sau đó ở trường hợp sau sẽ áp dụng mô hình code-behind
Trường hợp viết code ngay trong Razor Page
Pages/ViewProduct.cshtml
@page "/sanpham/{id:int?}" @functions { Product product; void OnGet() { int? ID = null; if (Request.RouteValues["id"] != null) { ID = Int32.Parse(Request.RouteValues["id"].ToString()); product = ProductContext.FindProductByID(ID.Value); } } } @{ this.OnGet(); } @if (product != null) { <h2>@product.Name</h2> <p>Mô tả: @product.Desciption</p> <p>Giá: @product.Price</p> <p> <a asp-page="ViewProduct" asp-route-id="">Sản phẩm khác</a> </p> } else { <h2>Các sản phẩm</h2> <ul> @foreach (var p in ProductContext.products) { <li> <a asp-page="ViewProduct" asp-route-id="@p.ID">@p.Name</a> </li> } </ul> }
Một số diễn giải cho đoạn code trên:
@page "/sanpham/{id:int?}"
Để định nghĩa route truy nhập tới Razor Page này, các URL phù hợp đó là /sanpham/
,
/sanpham/1
, /sanpham/2
...
Nếu truy cập /sanpham/1
thì id của route = 1, sẽ hiện thị thông tin sản phẩm có ID = 1,
id này đọc được ở đoạn code:
Request.RouteValues["id"]
Khối chỉ thị @functions
để khai báo phương thức, thuộc tính cho thêm vào lớp sinh ra bởi Razor.
Ở đây thêm thuộc tính product
và phương thức OnGet
(có chức năng lấy sản phẩm theo id route gửi đến).
Sau khi khai báo, phương thức này thi hành ở đoạn code
@{ this.OnGet(); }
Phần sau là mã HTML hiện thị thông tin sản phẩm, hoặc danh sách sản phẩm (khi truy cập /sanpham/)
Kết quả chạy:
Trường hợp áp dụng code-behide Razor Page
Đầu tiên tạo ra một lớp kế thừa từ PageModel
, lớp này khai báo trong thư mục cùng file Razor Page
và đặt tên giống Razor Page có thêm phần .cs
, ví dụ trên bạn sẽ tạo ra file ViewProduct.cshtml.cs
ViewProduct.cshtml.cs
chứa Model cho trang ViewProduct.cshtml
Nội dung trong ViewProduct.cshtml.cs
khai báo như sau:
using System; using Microsoft.AspNetCore.Mvc.RazorPages; using razor04.codebehide.Models; using static System.Console; namespace razor04.codebehide.Pages { // Lớp là Model của Razor, nên phải kế thừa PageModel public class ViewProductModel : PageModel { // Khai báo thuộc tính public Product product; // Handler chạy khi truy cập trang bằng phương thức get public void OnGet () { int? ID = null; if (Request.RouteValues["id"] != null) { ID = Int32.Parse (Request.RouteValues["id"].ToString ()); product = ProductContext.FindProductByID (ID.Value); } } } }
Để ứng dụng tự động tạo ra đối tượng lớp này khi truy cập trang Razor, thì trong Razor thêm vào dòng chỉ thị thiết lập Model của trang có kiểu ViewProductModel:
@model ViewProductModel
Lúc này truy cập trang một đối tượng lớp ViewProductModel tạo ra và truy cập được ở thuộc tính Model của Razor. Nội dụng của Razor Page (ViewProduct.cshtml) giờ có thể sửa là:
@page "/sanpham/{id:int?}" @using razor04.codebehide.Models; @model ViewProductModel @if (Model.product != null) { <h2>@Model.product.Name</h2> <p>Mô tả: @Model.product.Desciption</p> <p>Giá: @Model.product.Price</p> <p> <a asp-page="ViewProduct" asp-route-id="">Sản phẩm khác</a> </p> } else { <h2>Các sản phẩm</h2> <ul> @foreach (var p in ProductContext.products) { <li> <a asp-page="ViewProduct" asp-route-id="@p.ID">@p.Name</a> </li> } </ul> }
Kết quả chạy vẫn như trên. Tuy nhiên bạn đã phân tách các phần code chính xử lý ở một file Model và trang Razor chủ
yếu là hiện thị thông tin của Model. Qua ví dụ này lưu ý một số vấn để về sử dụng PageModel
như sau:
Xử lý yêu cầu gửi đến
Khi thiết lập một lớp (kế thừa từ PageModel) là Model của Razor Page thì khi truy vấn đến Page, Model này tự động tạo ra và Inject vào Razor. Nếu lớp Model đó có tham số khởi tạo là đối tượng dịch vụ, thì dịch vụ này phải đăng ký trong ServiceCollection của hệ thống.
Trong lớp Model có các phương thức có tiền tố là On như OnGet
, OnPost
, OnGetAsync
... thì chúng gọi là các handler. Phương thức này tự động chạy theo phương thức http truy cập (get, post, put ...).
Các thuộc tính trong PageModel đều có hiệu lực trong Razor, truy cập qua thuộc tính Model.
Các phương thức Handler trong PageModel
Như trên, các handler có tiền tố On
mặc định nó sử lý các yêu cầu theo các phương thức của http như
OnGet
, OnPut
... nếu bất đồng bộ thì OnGetAsync
, OnPostAsync
...
Các phương thức này trả về kiểu bất kỳ nhưng thường là void, Task hoặc ActionResult
Các phương thức này cũng có thể có các tham số, tham số này tự động truyền vào bởi giá trị query trong URL hay giá trị trường tương ứng của Route hoặc do phần từ cung trên của Form Post tới. Như ví dụ trên, nếu khai báo
void OnGet(int id) { /... }
Thì khi truy cập /sanpham/3
thì id sẽ bằng 3.
Các phương thức Named Handler trong PageModel
Bạn có thể khai báo nhiều Handler cùng phục vụ truy vấn đến theo một phương thức, ví dụ post. Tuy nhiên Handler cụ thể sẽ thi hành theo ham số handler gửi đến trong URL. Ví dụ, khai báo ba phương thức sau đều nhận yêu cầu post.
public String Thongbao; // Chạy truy cập post tới, url = /sanpham/2?handler=xoa public void OnPostXoa(int i) { Thongbao = "Gọi OnPostXoa"; } // Chạy truy cập post tới, url = /sanpham/2?handler=soanthao public void OnPostSoanthao(int id) { Thongbao = "Gọi OnPostSoanthao"; } // Chạy truy cập post tới, url = /sanpham/2?handler=xemchitiet public void OnPostXemchitiet(int id) { Thongbao = "Gọi OnPostXemchitiet"; }
Thêm mã vào Razor Page kiểm tra xem
<div class="d-flex"> <form method="post" asp-page-handler="xoa"> <button class="btn btn-danger m-1">Xóa</button> </form> <form method="post" asp-page-handler="soanthao"> <button class="btn btn-danger m-1">Soạn thảo</button> </form> <form method="post" asp-page-handler="xemchitiet"> <button class="btn btn-danger m-1">Xem chi tiết</button> </form> </div> <p class="alert alert-danger">@Model.Thongbao</p>
Hoặc
<form method="post" class="d-flex"> <button class="btn btn-danger m-1" asp-page-handler="xoa">Xóa</button> <button class="btn btn-danger m-1" asp-page-handler="soanthao">Soạn thảo</button> <button class="btn btn-danger m-1" asp-page-handler="xemchitiet">Xem chi tiết</button> </form>
Truyền dữ liệu từ PageModel đến Razor bằng ViewData
Trong PageModel và Razor cùng có thuộc tính ViewData có kiểu ViewDataDictionary
, nó dùng để chuyển dữ
liệu từ PageModel sang Razor, ví dụ nếu trong PageModel có gán:
public void OnGet (int id) { ViewData["mydata"] = "My Data"; //... }
Thì trong Razor đọc được giá trị này bằng @ViewData["mydata"]
Hander trả về IActionResult
Khi khai handler trả về kiểu IActionnResult thì có nhiều phương thức giúp tạo đối tượng trả về, tùy ngữ cảnh mà sử dụng:
Ví dụ, truy cập thì chuyển hướng sang trang khác:
public IActionResult OnGet (int id) { int? ID = null; if (Request.RouteValues["id"] != null) { ID = Int32.Parse (Request.RouteValues["id"].ToString ()); product = ProductContext.FindProductByID (ID.Value); if (product == null) { // Khônng thấy chuyển hướng về trang /product/ return RedirectToPage("ViewProduct", new { id = ""}); } } // Trả về kết quả trang Razor return Page(); }
Ngoài ra còn một số phương thực tạo IActionResult như:
Method | Mã HTTP | Mô tả |
---|---|---|
Challenge | 401 | Trả về khi xác thực không thành công |
Content | 200 | Thiết lập nội dung trả về trực tiếp |
File | 200 | Trả về nội dung file. |
Forbid | 403 | Cấm truy cập. |
LocalRedirect LocalRedirectPermanent LocalRedirectPreserveMethod LocalRedirectPreserveMethodPermanent |
302 301 307 308 |
Chuyển hướng |
NotFound | 404 | Không thấy. |
Page | 200 | Trả về nội dung trang Razor. |
Partial | 200 | Trả về nội dung từ một Partial Page |
RedirectToPagePermanent RedirectToPage RedirectToPagePreserveMethod RedirectToPagePreserveMethodPermanent |
301 302 307 308 |
Chuyển hướng |
Mã nguồn tham khảo: ASP_NET_CORE/razor04.codebehide