Thêm xác thực Validation

Trong phần này, bạn sẽ thêm logic xác thực vào Movie model và bạn sẽ đảm bảo rằng các quy tắc xác thực được thực thi bất kỳ lúc nào người dùng cố gắng tạo hoặc chỉnh sửa movie bằng ứng dụng.

Giữ mọi thứ DRY

Một trong những nguyên lý thiết kế cốt lõi của ASP.NET MVC là DRY (“Don’t Repeat Yourself”). ASP.NET MVC khuyến khích bạn chỉ định chức năng hoặc hành vi chỉ một lần, và sau đó nó được phản xạ ở mọi nơi trong một ứng dụng. Điều này làm giảm số lượng code bạn cần phải viết và viết ít lỗi hơn cũng như dễ bảo trì hơn.

Sự hỗ trợ xác thực được cung cấp bởi ASP.NET MVC và Entity Framework Code First là một ví dụ tuyệt vời về nguyên tắc DRY đang hoạt động. Bạn có thể khai báo quy tắc xác nhận hợp lệ ở một nơi (trong lớp model) và các quy tắc được thực thi ở mọi nơi trong ứng dụng.

Hãy xem cách bạn có thể tận dụng sự hỗ trợ xác thực hợp lệ này trong ứng dụng movie.

Thêm quy tắc xác thực vào Movie Model

Bạn sẽ bắt đầu bằng cách thêm một số logic xác nhận vào lớp Movie.

Mở tệp Movie.cs. Lưu ý System.ComponentModel.DataAnnotations không chứa System.Web. DataAnnotations cung cấp một tập hợp các thuộc tính xác nhận hợp lệ mà bạn có thể áp dụng khai báo cho bất kỳ lớp hoặc thuộc tính nào. (Nó cũng chứa các thuộc tính định dạng như DataType giúp định dạng và không cung cấp bất kỳ xác thực nào.)

Bây giờ hãy cập nhật lớp Movie để tận dụng các thuộc tính xác thực bắt buộc, StringLength, RegularExpression và Range được tích hợp sẵn. Thay thế lớp Movie bằng:

public class Movie
{
    public int ID { get; set; }

    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateTime ReleaseDate { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z'\s]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z'\s]*$")]
    [StringLength(5)]
    public string Rating { get; set; }
}

Thuộc tính StringLength thiết lập độ dài tối đa của chuỗi và nó đặt giới hạn này trên cơ sở dữ liệu, do đó lược đồ cơ sở dữ liệu sẽ thay đổi. Nhấp chuột phải vào bảng Movies trong Server explorer và nhấp vào Open Table Definition: Trong hình trên, bạn có thể thấy tất cả các trường chuỗi được đặt thành NVARCHAR (MAX). Chúng ta sẽ sử dụng migrations để cập nhật lược đồ. Biên dịch giải pháp, sau đó mở cửa sổ Package Manager Console và nhập các lệnh sau:

add-migration DataAnnotations update-database

Khi lệnh này kết thúc, Visual Studio sẽ mở tệp lớp định nghĩa lớp dẫn xuất DbMIgration mới với tên được chỉ định (DataAnnotations) và trong phương thức Up, bạn có thể thấy code cập nhật các ràng buộc lược đồ:

public override void Up()
{
    AlterColumn("dbo.Movies", "Title", c => c.String(maxLength: 60));
    AlterColumn("dbo.Movies", "Genre", c => c.String(nullable: false, maxLength: 30));
    AlterColumn("dbo.Movies", "Rating", c => c.String(maxLength: 5));
}

Trường Genre không còn trống (nghĩa là, bạn phải nhập một giá trị). Trường Rating có độ dài tối đa là 5 và Title có độ dài tối đa là 60. Độ dài tối thiểu trên Title là 3 và phạm vi trên Price không tạo thay đổi lược đồ.

Kiểm tra lược đồ Movie:

Các trường chuỗi hiển thị các giới hạn độ dài mới và Genre không còn được kiểm tra là rỗng nữa.

Các thuộc tính xác thực chỉ định hành vi bạn muốn thực thi trên thuộc tính model mà chúng được áp dụng. Các thuộc tính Required và MinimumLength chỉ ra rằng một thuộc tính phải có một giá trị; nhưng không có gì ngăn cản người dùng nhập khoảng trắng để đáp ứng xác thực này. Thuộc tính RegularExpression được sử dụng để giới hạn những ký tự có thể được nhập vào. Trong đoạn code trên, Genre và Rating chỉ được sử dụng các chữ cái (khoảng trắng, số và ký tự đặc biệt không được phép). Thuộc tính Range hạn chế giá trị trong phạm vi được chỉ định. Thuộc tính StringLength cho phép bạn thiết lập độ dài tối đa của một thuộc tính chuỗi và tùy chọn độ dài tối thiểu của nó. Các kiểu giá trị (chẳng hạn như decimal, int, float, DateTime) vốn được yêu cầu và không cần thuộc tính Required.

Code First đảm bảo rằng các quy tắc xác thực bạn chỉ định trên một lớp model được thực thi trước khi ứng dụng lưu các thay đổi trong cơ sở dữ liệu. Ví dụ, đoạn code dưới đây sẽ giải phóng một ngoại lệ DbEntityValidationException khi phương thức SaveChanges được gọi, vì một số giá trị thuộc tính Movie bắt buộc bị thiếu:


MovieDBContext db = new MovieDBContext();
Movie movie = new Movie();
movie.Title = "Gone with the Wind";
db.Movies.Add(movie);
db.SaveChanges();        // <= Will throw server side validation exception

Đoạn code trên giải phóng ngoại lệ sau:

Xác thực không thành công cho một hoặc nhiều thực thể. Xem thuộc tính ‘EntityValidationErrors’ để biết thêm chi tiết.

Việc có các quy tắc xác thực được thực thi bởi .NET Framework sẽ giúp ứng dụng của bạn mạnh mẽ hơn. Nó cũng đảm bảo rằng bạn không quên xác nhận một cái gì đó và vô tình để cho dữ liệu xấu vào cơ sở dữ liệu.

Lỗi xác thực UI trong ASP.NET MVC

Chạy ứng dụng và điều hướng đến /Movies URL.

Nhấp vào liên kết Create New để thêm movie mới. Điền vào biểu mẫu với một số giá trị không hợp lệ. Ngay sau khi xác thực phía máy khách jQuery phát hiện lỗi, nó sẽ hiển thị một thông báo lỗi.

Lưu ý

Để hỗ trợ xác thực jQuery cho các miền không phải tiếng Anh sử dụng dấu phẩy (“,”) cho dấu thập phân, bạn phải bao gồm NuGet globalizenhư được mô tả trước đây trong hướng dẫn này.

Lưu ý cách mà biểu mẫu đã tự động sử dụng đường viền màu đỏ để đánh dấu các hộp văn bản có chứa dữ liệu không hợp lệ và đã đưa ra một thông báo lỗi xác thực hợp lệ bên cạnh mỗi một văn bản. Các lỗi được thực thi cả phía máy khách (sử dụng JavaScript và jQuery) và phía máy chủ (trong trường hợp người dùng đã tắt JavaScript).

Một lợi ích thực sự là bạn không cần phải thay đổi một dòng code nào trong lớp MoviesController hoặc trong view Create.cshtml để kích hoạt giao diện người dùng xác thực này. Controller và các view mà bạn đã tạo trước đó trong hướng dẫn này tự động chọn các quy tắc xác thực đã được bạn chỉ định bằng cách sử dụng các thuộc tính xác thực trên các thuộc tính của lớp Movie model. Xác thực kiểm tra bằng cách sử dụng phương thức hành động Edit và xác thực tương tự được áp dụng.

Dữ liệu biểu mẫu không được gửi đến máy chủ cho đến khi không có lỗi xác thực phía máy khách. Bạn có thể xác minh điều này bằng cách đặt một điểm ngắt trong phương thức HTTP Post, bằng cách sử dụng công cụ fiddler hoặc các công cụ phát triển IE F12.

Cách mà xác thực xảy ra trong phương thức tạo hành động và tạo view

Bạn có thể tự hỏi làm thế nào giao diện người dùng (UI) xác nhận được tạo ra mà không có bất kỳ bản cập nhật nào cho code trong controller hoặc view. Danh sách tiếp theo cho thấy các phương thức Create trong lớp MovieController trông như thế nào. Chúng không thay đổi so với cách bạn tạo chúng trước đó trong hướng dẫn này.

public ActionResult Create()
{
    return View();
}
// POST: /Movies/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Movies.Add(movie);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}

Phương thức hành động tạo (HTTP GET) đầu tiên hiển thị biểu mẫu Create ban đầu. Phiên bản thứ hai ([HttpPost]) xử lý bài đăng biểu mẫu. Phương thức Create thứ hai (phiên bản HttpPost) gọi ModelState.IsValid để kiểm tra xem movie có bất kỳ lỗi xác thực nào không. Việc gọi phương thức này sẽ đánh giá bất kỳ thuộc tính xác thực nào đã được áp dụng cho đối tượng. Nếu đối tượng có lỗi xác thực, phương thức Create sẽ hiển thị lại biểu mẫu. Nếu không có lỗi, phương thức lưu movie mới vào cơ sở dữ liệu. Trong ví dụ movie của chúng ta, biểu mẫu không được đăng lên máy chủ khi có lỗi xác thực được phát hiện ở phía máy khách; phương thức Create thứ hai không bao giờ được gọi. Nếu bạn tắt JavaScript trong trình duyệt của mình, xác thực ứng dụng khách sẽ bị tắt và phương thức HTTP POST Create sẽ gọi ModelState.IsValid để kiểm tra xem movie có bất kỳ lỗi xác thực nào không.

Bạn có thể đặt điểm ngắt trong phương thức Tạo HttpPost và xác minh phương thức không bao giờ được gọi, xác thực phía máy khách sẽ không gửi dữ liệu biểu mẫu khi phát hiện lỗi xác thực. Nếu bạn tắt JavaScript trong trình duyệt của mình, sau đó gửi biểu mẫu có lỗi, điểm ngắt sẽ bị loại bỏ. Bạn vẫn nhận được xác nhận đầy đủ mà không cần JavaScript. Hình ảnh sau đây cho thấy cách tắt JavaScript trong Internet Explorer.

 

Những hình ảnh dưới đây cho thấy làm thế nào để vô hiệu hóa JavaScript trong trình duyệt FireFox.

 

Hình ảnh sau đây cho thấy cách tắt JavaScript trong trình duyệt Chrome.

 

Dưới đây là view template Create.cshtml mà bạn đã phác thảo trước đó trong hướng dẫn. Nó được sử dụng bởi các phương thức hành động được hiển thị ở trên cả hai để hiển thị biểu mẫu ban đầu và để hiển thị lại nó trong trường hợp có lỗi.

@model MvcMovie.Models.Movie
@{
    ViewBag.Title = "Create";
}

Create

@using (Html.BeginForm())  {     @Html.AntiForgeryToken()

 

Movie

 


@Html.ValidationSummary(true)

            @Html.LabelFor(model => model.Title, new { @class = “control-label col-md-2” })

                @Html.EditorFor(model => model.Title)                 @Html.ValidationMessageFor(model => model.Title)

 

@*Fields removed for brevity.*@

 

               

 

 

}

    @Html.ActionLink(“Back to List”, “Index”)

@section Scripts {     @Scripts.Render(“~/bundles/jqueryval”) }

Lưu ý cách mà code sử dụng một trình trợ giúp Html.EditorFor để xuất phần tử <input> cho mỗi thuộc tính Movie. Bên cạnh trình trợ giúp này là một cuộc gọi đến phương thức trợ giúp Html.ValidationMessageFor. Hai phương thức trợ giúp này làm việc với đối tượng model được controller truyền tới view(trong trường hợp này là một đối tượng Movie). Chúng sẽ tự động tìm kiếm các thuộc tính xác thực được chỉ định trên model và hiển thị thông báo lỗi khi thích hợp.

Điều thực sự tốt về cách tiếp cận này là cả controller lẫn view template Create đều không biết bất kỳ điều gì về các quy tắc xác thực thực tế đang được thi hành hoặc về các thông báo lỗi cụ thể được hiển thị. Các quy tắc xác thực và các chuỗi lỗi chỉ được chỉ định trong lớp Movie. Những quy tắc xác thực tương tự này sẽ tự động được áp dụng cho chế độ view Edit và bất kỳ view temaplate nào khác mà bạn có thể tạo để chỉnh sửa model của mình.

Nếu bạn muốn thay đổi logic xác nhận sau này, bạn có thể thực hiện điều đó một cách chính xác bằng cách thêm các thuộc tính xác thực vào model (trong ví dụ này là lớp movie). Bạn sẽ không phải lo lắng về các phần khác nhau của ứng dụng không phù hợp với cách các quy tắc được thực thi – tất cả các logic xác nhận sẽ được định nghĩa ở một nơi và được sử dụng cho mọi nơi. Điều này giữ code thuận tiện và làm cho nó dễ dàng để duy trì và phát triển. Và nó có nghĩa là bạn sẽ hoàn toàn tôn trọng nguyên tắc DRY.

Sử dụng thuộc tính DataType

Mở tệp Movie.cs và kiểm tra lớp Movie. System.ComponentModel.DataAnnotations cung cấp các thuộc tính định dạng ngoài tập hợp các thuộc tính xác thực hợp lệ. Chúng ta đã áp dụng giá trị kiểu liệt kê DataType cho ReleaseDate và các trường Price. Đoạn mã sau cho thấy các thuộc tính ReleaseDate và Price với thuộc tính DataType thích hợp.

Thuộc tính DataType chỉ cung cấp gợi ý cho công cụ xem để định dạng dữ liệu (và cung cấp các thuộc tính như <a> cho URL và <a href=”mailto:EmailAddress.com”> cho email. Bạn có thể sử dụng thuộc tính RegularExpression để xác nhận hợp lệ định dạng của dữ liệu: Thuộc tính DataType được sử dụng để xác định một kiểu dữ liệu cụ thể hơn cơ sở dữ liệu nội tại, chúng không phải là các thuộc tính xác thực, trong trường hợp này chúng ta chỉ muốn theo dõi ngày tháng, không phải ngày tháng và thời gian. Kiểu liệt kê DataType cung cấp cho nhiều kiểu dữ liệu, như Date, Time, PhoneNumber, Currency, EmailAddress và hơn thế nữa. Thuộc tính DataType cũng có thể cho phép ứng dụng tự động cung cấp các tính năng kiểu cụ thể, ví dụ, một liên kết mailto: có thể được tạo ra cho DataType.EmailAddress, và một bộ chọn ngày có thể được cung cấp cho DataType.Date trong các trình duyệt hỗ trợ HTML5. Các thuộc tính DataType cung cấp các thuộc tính HTML 5 (các dấu gạch ngang dữ liệu được phát âm) mà trình duyệt HTML 5 có thể hiểu được. DataType không cung cấp bất kỳ xác thực nào.

DataType.Date không chỉ định định dạng của ngày được hiển thị. Theo mặc định, trường dữ liệu được hiển thị theo các định dạng mặc định dựa trên CultureInfo của máy chủ.

Thuộc tính DisplayFormat được sử dụng để chỉ định rõ định dạng ngày:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }

Cài đặt ApplyFormatInEditMode quy định rằng định dạng được chỉ định cũng nên được áp dụng khi giá trị hiển thị trong hộp văn bản để chỉnh sửa. (Bạn có thể không muốn điều đó cho một số trường – ví dụ, đối với các giá trị tiền tệ, bạn có thể không muốn ký hiệu tiền tệ trong hộp văn bản để chỉnh sửa.)

Bạn có thể sử dụng thuộc tính DisplayFormat của chính nó, nhưng đó thường là một ý tưởng tốt để sử dụng thuộc tính DataType. Thuộc tính DataType truyền tải ngữ nghĩa của dữ liệu trái với cách hiển thị nó trên màn hình và cung cấp các lợi ích sau mà bạn không nhận được với DisplayFormat:

  • Trình duyệt có thể bật các tính năng HTML5 (ví dụ như để hiển thị một điều khiển lịch, ký hiệu tiền tệ địa phương thích hợp, liên kết email, v.v.).
  • Theo mặc định, trình duyệt sẽ hiển thị dữ liệu bằng định dạng chính xác dựa trên ngôn ngữ của bạn.
  • Thuộc tính DataType có thể cho phép MVC chọn mẫu trường đúng để kết xuất dữ liệu (DisplayFormat nếu được sử dụng bởi chính nó sử dụng mẫu chuỗi). Để biết thêm thông tin, xem mẫu ASP.NET MVC 2 của Brad Wilson. (Mặc dù được viết cho MVC 2, bài viết này vẫn áp dụng cho phiên bản hiện tại của ASP.NET MVC.)

Nếu bạn sử dụng thuộc tính DataType với trường ngày, bạn phải chỉ định thuộc tính DisplayFormat cũng để đảm bảo rằng trường hiển thị chính xác trong trình duyệt Chrome. Để biết thêm thông tin, hãy xem luồng StackOverflow này.

Lưu ý

Xác thực jQuery không hoạt động với thuộc tính Range và DateTime. Ví dụ: code sau sẽ luôn hiển thị lỗi xác thực phía ứng dụng, ngay cả khi ngày nằm trong phạm vi được chỉ định:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

Bạn sẽ cần vô hiệu hóa xác thực ngày jQuery để sử dụng thuộc tính Range với DateTime. Nói chung không phải là một cách hay để biên dịch ngày tháng khó khăn trong các model của bạn, vì vậy việc sử dụng thuộc tính Range và DateTime không được khuyến khích.

Đoạn mã sau đây cho thấy các thuộc tính kết hợp trên một dòng:

public class Movie
{
   public int ID { get; set; }
   [Required,StringLength(60, MinimumLength = 3)]
   public string Title { get; set; }
   [Display(Name = "Release Date"),DataType(DataType.Date)]
   public DateTime ReleaseDate { get; set; }
   [Required]
   public string Genre { get; set; }
   [Range(1, 100),DataType(DataType.Currency)]
   public decimal Price { get; set; }
   [Required,StringLength(5)]
   public string Rating { get; set; }
}

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *