ASP.NET - MVC - Phương thức Create
Tìm hiểu phương thức Create và View Create
1. Tùy chỉnh View
Đầu tiên, chúng ta sửa giao diện Tạo Sách mới một chút. Mở Views > Product > Create.cshtml
Ở đây mỗi khi bạn tạo 1 Controller thì ASP.NET sẽ tạo 1 folder trong Views, mỗi Folder này sẽ chứa các file *.cshtml tương ứng với các Action trong Controller.
Các bạn sửa file như sau:
@using (Html.BeginForm("Create","Book",FormMethod.Post, new { enctype = "multipart/form-data" } ))
{
...
}
Ở đây, tham số đầu tiên sẽ là Action khi submit form, tham số thứ 2 là Controller xử lý, tham số thứ 3 là phương thức của form, cuối cùng là dạng để có thể truyền nhiều kiểu dữ liệu lên, ở đây mình truyền file ảnh và text nên cần dùng đến mutipart/form-data.
Tiếp theo bạn thay đổi Image thành kiểu type = “file”
@Html.TextBoxFor(model => model.Images, null, new { type = "file", @class = "form-control" })
Thay đổi "AuthorID" trong HTML sau:
@Html.LabelFor(model => model.AuthorID, "AuthorID", htmlAttributes: new { @class = "control-label col-md-2" })
Thành "Tác giả"
@Html.LabelFor(model => model.AuthorID, "Tác giả", htmlAttributes: new { @class = "control-label col-md-2" })
Thay đổi "CategoryID" trong HTML sau:
@Html.LabelFor(model => model.CategoryID, "CategoryID", htmlAttributes: new { @class = "control-label col-md-2" })
Thành "Chủ đề"
@Html.LabelFor(model => model.CategoryID, "Chủ đề", htmlAttributes: new { @class = "control-label col-md-2" })
Thay đổi @Html.EditorFor thành @Html.TextAreaFor chỗ mô tả sách như sau:
@Html.TextAreaFor(model => model.Description, new { htmlAttributes = new { @class = "form-control" } })
Chèn FCKEditor cho mô tả:
<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, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
<script>
CKEDITOR.replace("Description")
</script>
</div>
</div>
Chèn thêm nút Reset vào kế bên nút submit:
<input type="reset" value="Làm lại" class="btn btn-danger" />
Cuối cùng chúng ta sắp xếp lại các dòng nhập cho hợp lý. Mã nguồn tậ tin Create.cshtml
@model BookStoreManager.Models.Book
@{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<style>
.width-text-box {
width: 160px;
}
</style>
<script src="~/Ckeditor/ckeditor.js"></script>
@using (Html.BeginForm("Create","Book",FormMethod.Post, new { enctype = "multipart/form-data" } ))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>Thêm sách mới @ViewBag.Message </h2>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<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" })
@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.EditorFor(model => model.Description, new { htmlAttributes = new { @class = "form-control, @cols = 35, @rows = 3" } })
@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.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.
Khi chạy chương trình sẽ gọi phương thức public ActionResult Create() trong controller BookController.cs:
public ActionResult Create()
{
ViewBag.AuthorID = new SelectList(db.Authors, "AuthorID", "AuthorName");
ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "CategoryName");
return View();
}
Ở đây ta thấy có hai ViewBag.AuthorID, ViewBag.CategoryID để chứa hai bảng Author và Categories cho View Create.cshtml
Cuối cùng, kết quả sau khi tùy chỉnh View Create.cshtml
Mở mã nguồn trang Create chúng ta thấy:
<form action="/Book/Create" enctype="multipart/form-data" method="post"><input name="__RequestVerificationToken" type="hidden" value="gF4ls0fY8IfmfSrbee6nwn0drbbgSWoMjdJPOq0xpyC5bgKQK0QholA-ldSMEHYfKafKDPnJCWJeHYLrcIPQ9wCpIiDzZN5Q95vunTfGo6w1" /><div class="form-horizontal">
<h2>Thêm sách mới </h2>
<hr />
.........
</form>
<div>
Để ý phần tử form, bạn thấy thuộc tính value có giá trị “gF4ls0fY8IfmfSrbee6nwn0drbbg..” 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 hàm Create():
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "BookID,Title,AuthorID,Price,Images,CategoryID,Description,Published,ViewCount")] Book book)
{
if (ModelState.IsValid)
{
db.Books.Add(book);
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 Create bằng phương thức Create mới sau: (copy đè phương thức Create cũ, Xem phương thức mới khác phương cũ chỗ nào):
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create([Bind(Include = "BookID,Title,AuthorID,Price,Images,CategoryID,Description,Published,ViewCount")] Book book, HttpPostedFileBase Images)
{
if (ModelState.IsValid)
{
try
{
if (Images.ContentLength > 0)
{
string _FileName = Path.GetFileName(Images.FileName);
string _path = Path.Combine(Server.MapPath("~/bookimages"), _FileName);
Images.SaveAs(_path);
book.Images = _FileName;
}
db.Books.Add(book);
db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
ViewBag.Message = "không thành công!!";
}
}
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. 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.
- Các bạn cần tạo folder Image trong Project để chứa ảnh
- Đồ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: