ASP.NET - MVC - Phương thức Edit
Tìm hiểu về các phương thức Edit và View Edit
Cách tùy chỉnh giống View Create.cshtml
1. Tùy chỉnh View
Các bước giống tùy chỉnh View Create.cshtml
Trong View Edit ta thêm thẻ <img src="~/bookimages/@Model.Images" /> để hiển thị hình sách như sau:
<div class="form-group">
@Html.LabelFor(model => model.Images, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.TextBoxFor(model => model.Images, null, new { type = "file", @class = "form-control" })
<img src="~/bookimages/@Model.Images" />
@Html.ValidationMessageFor(model => model.Images, "", new { @class = "text-danger" })
</div>
</div>
Tiếp theo, trong View Edit ta thêm thẻ @Html.Hidden("oldimage", @Model.Images) để lưu ảnh:
<div class="form-group">
@Html.LabelFor(model => model.Images, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.TextBoxFor(model => model.Images, null, new { type = "file", @class = "form-control" })
<img src="~/bookimages/@Model.Images" />
@Html.Hidden("oldimage", @Model.Images)
@Html.ValidationMessageFor(model => model.Images, "", new { @class = "text-danger" })
</div>
</div>
Mã nguồn trang Edit.cshtml sau khi tùy chỉnh
@model BookStoreManager.Models.Book
@{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<style>
.width-text-box {
width: 160px;
}
</style>
<script src="~/Ckeditor/ckeditor.js"></script>
@using (Html.BeginForm("Edit", "Book", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>Chỉnh sửa sách</h2>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.BookID)
<div class="form-group">
@Html.LabelFor(model => model.CategoryID, "Chủ đề", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("CategoryID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.CategoryID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Title, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Price, htmlAttributes: new { @class = "control-label col-md-2 " })
<div class="col-md-10">
@Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control width-text-box" } })
@Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Images, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.TextBoxFor(model => model.Images, null, new { type = "file", @class = "form-control" })
<img src="~/bookimages/@Model.Images" />
@Html.Hidden("oldimage", @Model.Images)
@Html.ValidationMessageFor(model => model.Images, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Published, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Published, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Published, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ViewCount, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ViewCount, new { htmlAttributes = new { @class = "form-control width-text-box" } })
@Html.ValidationMessageFor(model => model.ViewCount, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Description, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.TextAreaFor(model => model.Description)
@Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
<script>
CKEDITOR.replace("Description")
</script>
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.AuthorID, "Tác giả", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("AuthorID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.AuthorID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Thêm mới" class="btn btn-success" />
<input type="reset" value="Làm lại" class="btn btn-danger" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Trở về danh sách", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
Trong đoạn mã trên, bạn để ý thấy @Html.HiddenFor(model => model.BookID) để lưu mã sách dùng cho cập nhật, @Html.AntiForgeryToken() đóng vai trò hiển thị các token bảo mật, còn @Html.ValidationSummary(true) là dùng để hiển thị các thông báo kiểm chứng dữ liệu. Ví dụ, bạn có 1 trường input kiểu số, nhưng bạn lại nhập vào kiểu chuỗi thì hệ thống sẽ báo lỗi.
Từ trang Index.cshtml. Sau đó, nhấp liên kết Sửa trên giao diện Web để đi đến trang chỉnh sửa liên kết http://localhost:xxxx/Book/Edit/13 sẽ gọi ActionResult Edit(int? id) trong controller BookController.cs Cho kết quả sau khi tùy chỉnh View Edit.cshtml.
Mã nguồn của đường dẫn http://localhost:xxxx/Book/Edit/13
<form action="/Book/Edit/13" enctype="multipart/form-data" method="post"><input name="__RequestVerificationToken" type="hidden" value="86rsT3URPJ9L-hZPc4KKxIK2J2rDniFZf3zhGbdtN496nTUumnPQ7iq1I9fOlNMaZpeIlNFdrOZjvMvpI16D9Me8GzRGz9zoT7KG6xneBFY1" /> <div class="form-horizontal">
<h2>Chỉnh sửa sách</h2>
.......
</form>
Để ý phần tử form, bạn thấy thuộc tính value có giá trị “86rsT3URPJ9L-hZPc4KKxIK2J2rDniFZ..” thì đây chính là giá trị token ẩn (tự động gieo mỗi lần duyệt Web) để bảo mật cho trang web. Nếu một hacker khi submit dữ liệu thông qua form Edit mà không gửi kèm đúng token thì không thể ghi đè vào database. Cơ chế này làm cho các ứng dụng ASP.NET MVC cực kỳ bảo mật, vì vậy rất thích hợp để áp dụng cho các hệ thống hoạt động ngân hàng hay thanh toán điện tử.
2. Tùy chỉnh controller
Mở tập tin BookController.cs. Tiếp theo chúng ta sẽ xử lý trong Controller như sau, tìm đến hai phương thức Edit():
2.1 Phương thức Edit dạng GET
// GET: Book/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = db.Books.Find(id);
if (book == null)
{
return HttpNotFound();
}
ViewBag.AuthorID = new SelectList(db.Authors, "AuthorID", "AuthorName", book.AuthorID);
ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "CategoryName", book.CategoryID);
return View(book);
}
Phương thức Edit ở dạng get dùng để hiển thị nội dung của một quyển sách cần sửa. Phương thức Edit dùng chỉ số id để dò tìm BookID trong bảng Book. Ví dụ đường dẫn http://localhost:xxxx/Book/Edit/13 có nghĩa là hiển thị thông tin Book có chỉ số BookID= 13, nếu không tìm thấy thì sẽ hiển thị trống. Trong hình sau, bạn có thể thấy nội dung đường dẫn http://localhost:xxxx/Book/Edit/13.
Ở đây ta thấy. Nếu id và book là null (không tồn tại) thì MVC ném ra các lỗi return new HttpStatusCodeResult(HttpStatusCode.BadRequest) và return HttpNotFound(), Ngoài ra chúng ta còn có ViewBag.AuthorID, ViewBag.CategoryID để chứa hai bảng Author và Categories cho View Create.cshtml
Ngoài lề 1 tý, kiểu int? có nghĩa là biến có thể là int hoặc là null.
int i1 = 1; //OK
int i2 = null; //not OK
int? i3 = 1; //OK
int? i4 = null; //OK, int? cho phép biến bằng null
Nếu một Book được tìm thấy theo chỉ số id, một thể hiện mô hình Book được đẩy sang View bằng dòng return View(book).
2.2 Phương thức Edit dạng POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "BookID,Title,AuthorID,Price,Images,CategoryID,Description,Published,ViewCount")] Book book)
{
if (ModelState.IsValid)
{
db.Entry(book).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.AuthorID = new SelectList(db.Authors, "AuthorID", "AuthorName", book.AuthorID);
ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "CategoryName", book.CategoryID);
return View(book);
}
Tùy chỉnh phương thức Edit bằng phương thức Edit mới sau: (copy đè phương thức Edit cũ, Xem phương thức mới khác phương cũ chỗ nào):
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Edit([Bind(Include = "BookID,Title,AuthorID,Price,Images,CategoryID,Description,Published,ViewCount")] Book book, HttpPostedFileBase Images,FormCollection form)
{
if (ModelState.IsValid)
{
try
{
if (Images!=null)
{
string _FileName = Path.GetFileName(Images.FileName);
string _path = Path.Combine(Server.MapPath("~/bookimages"), _FileName);
Images.SaveAs(_path);
book.Images = _FileName;
// get Path of old image for deleting it
_path = Path.Combine(Server.MapPath("~/bookimages"), form["oldimage"]);
if (System.IO.File.Exists(_path))
System.IO.File.Delete(_path);
}
else
book.Images = form["oldimage"];
db.Entry(book).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
ViewBag.Message = "không thành công!!";
}
return RedirectToAction("Index");
}
ViewBag.AuthorID = new SelectList(db.Authors, "AuthorID", "AuthorName", book.AuthorID);
ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "CategoryName", book.CategoryID);
return View(book);
}
Như đã giải thích ở trên, thuộc tính ValidateAntiForgeryToken kiểm chứng token XSRF (Cross-site request forgery) được gieo bởi cuộc gọi @Html.AntiForgeryToken() ở view, [ValidateInput(false)] cho phép chúng ta truyền dữ liệu có chứa các thẻ html, css, javascript. Mô hình ASP.NET MVC lấy các giá trị cần post ở form, sau đó tạo một đối tượng Link, đẩy các giá trị này vào và kiểm chứng dữ liệu bằng phương thức ModelState.IsValid
Trong code trên chúng ta chưa kiểm tra loại tập tin được upload lên Server. Bây giờ, chúng ta cũng tìm hiểu code nhé!
Lưu đường dẫn của ảnh vào thuộc tính Images
- ValidateAntiForgeryToken kiểm chứng token XSRF (Cross-site request forgery) được gieo bởi cuộc gọi @Html.AntiForgeryToken() ở view.
- Mô hình ASP.NET MVC lấy các giá trị cần post ở form, sau đó tạo một đối tượng Book, đẩy các giá trị này vào và kiểm chứng dữ liệu bằng phương thức ModelState.IsValid
- Đầu tiên là các tham số truyền vào trong Action, tham số book chính là các giá trị trong form được binding vào đối tượng product này đấy, bao gồm BookID,Title,AuthorID,Price,Images,CategoryID,Description,Published,ViewCount, còn tham số thứ 2 chính là phần ảnh mình upload lên cùng trong form, ASP.NET hỗ trợ kiểu HttpPostedFileBase để nhận dạng file upload. FormCollection để lấy hình lưu tạm tên oldimage. Lưu ý tên tham số thứ 2 phải cùng với tên trong View. Trong trường hợp này là Images.
- Khi upload hình mới sẽ xóa hình cũ bằng lệnh sau System.IO.File.Delete(_path). Trước khi xóa kiểm tra xem có hình trong thư mục? bằng lệnh System.IO.File.Exists(_path)
- Đồng thời mình lưu file vào trong thư mực /bookimages của project .
- Sau khi thực hiện xong, mình sẽ dùng BookStoreEntity để lưu dữ liệu vào trong CSDL. Bạn có thể hiểu nó là lớp trung gian để thao tác với CSDL được Entity Framework xây dựng nên thôi.
- Dữ liệu Book được lưu vào database thông qua phương thức SaveChanges().
- Trong trường hợp upload hình hoặc lưu dữ liệu khôn thành công thì chúng ta hiển thị thông báo lỗi thông qua ViewBag.Message = "không thành công!!";
Các bạn vào Database để kiểm tra dữ liệu đã được lưu thành công hay chưa, và đây là kết quả của mình: