Model Validation trong ASP.NET Core

Bài viết này chúng ta sẽ học về cơ chế Model Validation. Thường thì dữ liệu được nhập bởi người dùng sẽ không hợp lệ và không thể đưa vào cơ sở dữ liệu. Các dữ liệu được nhập có thể chứa một số lỗi chính tả hoặc người dùng cố tính nhập các dữ liệu không phù hợp. Vì thế chúng ta cần phải kiểm tra dữ liệu người dùng nhập vào trước khi lưu trữ vào cơ sở dữ liệu. ASP.NET Core cung cấp chó chúng ta thành phần gọi là Model Validator, nó dùng các attribute để kiểm tra dữ liệu trong model dễ dàng hơn. Chúng ta cũng tìm hiểu về ModelState và cách sử dụng nó.

Giới thiệu về Model Validation

Form Data post dữ liệu lên Controller action tự động được map vào các tham số của action bởi Model Binder như chúng ta đã tìm hiểu ở bài trước. Model cần kiểm tra dữ liệu đầu vào xem có hợp lệ không. Quá trình kiểm tra này có thể được hoàn thành bởi client trước khi gửi lên server hoặc server kiểm tra sau khi nhận được từ client. Cơ chế validation phía client (client-side validation) rất quan trọng vì nó giúp tăng trải nghiệm người dùng khi kiểm tra dữ liệu mà không cần chờ đến server nhưng phía server lại nên đảm bảo một lần nữa để các dữ liệu không hợp lệ không thể đưa vào hệ thống.

Sự quan trọng của client-side validation

  • Giúp tăng sự trải nghiệm
  • Vì việc kiểm tra tiến hành phía trình duyệt client nên phản hồi nhanh hơn và gần như là ngay lập tức
  • Tiết kiệm tài nguyên server như là băng thông bằng cách giảm truy vấn đến server.

Sự quan trọng của server-side validation

Client-side validation cung cấp trải nghiệm người dùng tốt hơn nhưng không tin cậy. Nó có thể lỗi do một trong các lý do sau:

  • Javascript có thể bị tắt ở trình duyệt
  • Người dùng có thể gửi trực tiếp dữ liệu đến người dùng mà không sử dụng ứng dụng hoặc sử dụng một số các trình chỉnh sửa request có hại.
  • Khi Javascript có lỗi thì kết quả là dữ liệu được đưa vào hệ thống mà có thể không hợp lệ

Vì thế điều quan trọng là kiểm tra dữ liệu phải được thực hiện ở cả phía server, ngay cả bạn đã validate ở phía client.

Kiểm tra model một cách tường minh

Một khi bạn nhận model trong controller, bạn có thể kiểm tra model đó bằng cách viết code như sau:

if (string.IsNullOrEmpty(model.Name)) 
{ 
    //Validation Failed
    //Send the list of errors to client
}

Đoạn code trên đơn thuần chỉ kiểm tra xem thuộc tính name của model có rỗng hay null không. Đoạn code trên làm việc tốt, nhưng bạn sẽ phải làm nhiều lần tương tự nếu có nhiều thuộc tính trong model, khi đó bạn cần kiểm tra với các đoạn code giống nhau. Ở đây bạn cần xem làm sao để gửi các báo lỗi về client để hiển thị cho người dùng. Model Validator sẽ làm điều này cho bạn mà không cần viết các đoạn code thừa thãi.

Model Validation làm việc ra sao?

Chúng đã tìm hiểu cơ chế Model Binding làm việc ra sao trong ASP.NET Core rồi đúng không? Vậy khi HTTP Request được đưa tới Model Binder thì nó sẽ được gọi trước khi truyền tham số vào Controller action. Model Binder sẽ không chỉ map dư liệu vào action method mà nó còn kiểm tra chúng sử dụng Model Validator. 

Model Validator chạy sau model binding và chạy một loạt các logic kiểm tra trên mỗi thuộc tính của model dựa trên các attribute bạn đặt cho các thuộc tính đó. Các attribute này gọi là Validation Attribute và chứa code sử dụng bởi Model Validator.

 

Tất cả các logic kiểm tra đều chạy phía server. ASP.NET Core có nhiều các attribute viết sẵn, bạn có thể thêm nó vào thuộc tính của model để cài đặt quy tắc kiểm tra. Các quy tắc này cũng được gọi là DataAnnotations và nằm trong namespace: System.ComponentModel.DataAnnotations. Bạn cần import nó vào ViewModel và sử dụng.

Ví dụ, bạn đặt trường bắt buộc phải nhập sẽ dùng [Required]

[Required]
Public string Name {get;set;}

Model Binder không bắn ra bất cứ lỗi nào nếu quá trình kiểm tra không hợp lệ. Nhưng nó sẽ cập nhật đối tượng ModelState với danh sách lỗi và đặt thuộc tính IsValid false trước khi gọi action method. Chúng ta cần kiểm tra ModelState.IsValid để biết xem quá trình kiểm tra có hợp lệ hay không để action method có thể trả về danh sách lỗi nếu cần.

if (ModelState.IsValid)
{ 
    //Model is valid. Call Service layer for further processing of data
} else {
    //Validation failed. Return the model to the user with the relevant error messages
}

Cách sử dụng Validation Attributes

Cập nhật Model với Data Annotaion

Trong Model, thuộc tính thêm vào Data Annotation attribute được hiển thị bên dưới đây. Code sẽ được thêm vào attribute cho thuộc tính Name. Nó cũng được cài đặt thông báo trong trường hợp kiểm tra dữ liệu không hợp lệ với attribute đó:

[Required(AllowEmptyStrings =false,ErrorMessage ="Please enter the name")]
[StringLength(maximumLength:25,MinimumLength =10,ErrorMessage ="Length must be between 10 to 25")]
public string Name { get; set; }

Hiển thị danh sách lỗi Validations

Trong view, sử dùng Validation Tag Helper để hiển thị lỗi cho người dùng. Tag helper asp-validation-summary giúp hiển thị danh sách các thông báo lỗi cho riêng Model validation trên form. Nó được gắn vào thẻ div và đặt trên cùng của form. asp-validation-for hiển thị danh sách lỗi cho thuộc tính Name ở phía bên phải của nó. Nó được gắn vào thẻ span và đặt ngay cạnh thuộc tính.

Validation tag helper thêm class field-validation-errorvalidation-summary-errors vào HTML nếu lỗi tìm thấy. Vì thế chúng a thêm các style CSS vào để hiển thị lỗi màu đỏ cho các class này. Bạn có thể đọc thêm phần Validation Tag Helper ở bài sau:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model ASPNetCoreForms.Models.ProductEditModel

@{
    ViewData["Title"] = "Create";
}

<style>
    .field-validation-error { 
        color: red
    }
    .validation-summary-errors {
        color: red
    }
</style>

<h2>Create</h2>

<form action="/home/create" method="post">

    <div asp-validation-summary="ModelOnly">
        <span>Please correct the following errors</span>
    </div>

    <label asp-for="Name">Name</label>
    <input asp-for="Name" />
    <span asp-validation-for="Name"></span>
    <br />

    <input type="submit" name="submit" />
</form>

Kiểm tra ModelState.IsValid trong Controller Action

Cuối cùng trong Controller action method chúng ta kiểm tra nếu ModelState.IsValid nếu có bất cứ lỗi nào thì chúng ta trả về cho người dùng. Chúng ta trả về model để hiển thị các giá trị được nhập đồng thời trả kèm cả danh sách lỗi được hiển thị bởi Validation tag helper:

[HttpPost]
public IActionResult Create(ProductEditModel model)
{
    string message = "";

    if (ModelState.IsValid)
    {
         message = "product " + model.Name + " created successfully";
    }
    else
    {
        return View(model);
    }
    return Content(message);
}

Đoạn code trên sẽ hiển thị như sau:

 

ModelState

Model Binder cập nhật đối tượng ModelState với kết quả của Model Binding và validation. ModelState sẽ lưu chi tiết của các giá trị được cập nhật lên model và cả các thông tin lỗi trong quá trình validation xuất hiện trong mỗi thuộc tính. ModelState là thuộc tính của ControllerBase và thuộc kiểu ModelStateDictionary.

Các thuộc tính của ModelState

Các phương thức của ModelState

AddModelError

Thêm một message lỗi cụ thể vào danh sách Errors với 1 key.

Clear

Xoá toàn bộ các key và value trong thể hiện của ModelStateDictionary

ClearValidationState(string)

Xoá phần tử trong ModelStateDictionary đúng với key truyền vào

GetFieldValidationState(string)

Trả về ModelValidationState với key truyền vào

TryAddModelError(string, Exception, ModelMetadata)

Thêm một exception cụ thể vào Errors với một key

Ví dụ sử dụng ModelState

Lấy danh sách Errors

Lặp quá danh sách key và lấy danh sách Errors trong ModelState

foreach (var modelStateKey in ModelState.Keys)
{
    var modelStateVal = ModelState[modelStateKey];
    foreach (var error in modelStateVal.Errors)
    {
        var key = modelStateKey;
        var errorMessage = error.ErrorMessage;
    }
}

Thêm một Custom Error Message

Không phải tất cả các thông báo lỗi đều được đưa ra bởi Model Validator. Ví dụ bạn nhận một Product Model hợp lệ nhưng Product lại tồn tại trong cơ sở dữ liệu. Vậy trường hợp này bạn cần thêm vào một custom error message cho ModelState và gửi nó cho người dùng:

if (ModelState.IsValid) { 
    If (someService.IsProductExists(model)) { 
        ModelState.AddModelError("", “Product already exists”); 
        return View(model); 
    } else { 
        return View(model); 
    } 
}

Danh sách Validation Attributes

Chúng ta nhìn 2 attribute Required StringLength ở ví dụ trước. Namespace System.ComponentModel.DataAnnotaions chứa một số các thuộc tính. Đây là danh sách:

CreditCard

Kiểm tra thuộc tính có định dạng credit card

[CreditCard(ErrorMessage ="Please enter a valid card No")]
public string creditCard { get; set; }

Compare

So sánh giá trị với giá trị của thuộc tính khác

Ví dụ, áp dụng vào trường hợp cần xác nhận mật khẩu. Tham số vào của attribute này là NewPassword. Nếu xác nhận mật khẩu không đúng với NewPassword thì quá trình kiểm tra không hợp lệ.

[Required(AllowEmptyStrings =false,ErrorMessage ="Please enter a valid password")]
public string NewPassword { get; set; }

[Compare(otherProperty:"NewPassword", ErrorMessage ="New & confirm password does not match")]
public string ConfirmPassword { get; set; }

Cách khác là bạn có thể so sánh trong một phương thức get để kiểm tra:

[Required(AllowEmptyStrings = false, ErrorMessage = "Please enter a valid password")]
public string NewPassword { get; set; }

[Compare(otherProperty: "validatePassword", ErrorMessage = "New & confirm password does not match")]
public string ConfirmPassword { get; set; }

public string validatePassword
{
    get
    {
       //Do Some calculations here 
       return this.NewPassword;
    }
}

EmailAddress

Kiểm tra thuộc tính có phải định dạng email không

[EmailAddress(ErrorMessage ="Please enter a valid email")]
public string EmailID {get;set;}

HTML được tạo ra:

<label for="EmailID">EmailID</label>
<input type="email" data-val="true" data-val-email="Please enter a valid email" id="EmailID" name="EmailID" value="" />
<span class="field-validation-valid" data-valmsg-for="EmailID" data-valmsg-replace="true"></span>
<br />

Như bạn thấy HTML, thuộc tính type của nó sẽ tạo ra là email. Trong trường hợp này thì trình duyệt sẽ hiển thị thông báo lỗi nếu có một email không hợp lệ.

Phone

Kiểm tra định dạng số điện thoại

[Phone(ErrorMessage = "Please enter a valid Phone No")]
public string PhoneNo { get; set; }
<label asp-for="PhoneNo"></label>
<input asp-for="PhoneNo" />
<span asp-validation-for="PhoneNo"></span>
<label for="PhoneNo">PhoneNo</label>
<input type="tel" data-val="true" data-val-phone="Please enter a valid Phone No" id="PhoneNo" name="PhoneNo" value="" />
<span class="field-validation-valid" data-valmsg-for="PhoneNo" data-valmsg-replace="true"></span>
<br />

Range

Cho phép kiểm tra xem giá trị có nằm trong một khoảng cho trước không

[Range(minimum:100,maximum:200, ErrorMessage = "Please enter a valid no between 100 & 200")]
public int Range { get; set; }
<label asp-for="Range"></label>
<input asp-for="Range" />
<span asp-validation-for="Range"></span>
<label for="Range">Range</label>
<input type="number" data-val="true" data-val-range="Please enter a valid no between 100 &amp; 200" data-val-range-max="200" data-val-range-min="100" data-val-required="The Range field is required." id="Range" name="Range" value="0" />
<span class="field-validation-valid" data-valmsg-for="Range" data-valmsg-replace="true"></span>
<br />

RegularExpression

Kiểm tra dữ liệu có khớp với một biểu thức chính quy không

[RegularExpression(pattern: "^Mr\\..*|^Mrs\\..*|^Ms\\..*|^Miss\\..*", ErrorMessage ="Name must start with Mr./Mrs./Ms./Miss.")]
public string FullName { get; set; }

Required

Yêu cầu một thuộc tính là bắt buộc. Các kiểu không chấp nhận giá trị null (decimal, int, float và DateTime) thường mặc định mà bắt buộc không cần dùng đến attribute này. Ứng dụng sẽ tự động kiểm tra phía server với các kiểu không cho phép null và tự đánh là Required.

StringLength

Kiểm tra xem chuỗi có nằm trong vùng độ dài cho phép

[StringLength(MaximumLength: 25, MinimumLength = 10, ErrorMessage = "Length must be between 10 to 25")]
public string Name { get; set; }

Url

Kiểm tra xem định dạng URL chuẩn không

[Url(ErrorMessage ="Please enter a valid URL")]
public string Url { get; set; }
<label asp-for="Url"></label>
<input asp-for="Url" />
<span asp-validation-for="Url"></span>

HTML tạo ra là

<label for="Url">Url</label>
<input type="url" data-val="true" data-val-url="Please enter a valid URL" id="Url" name="Url" value="" />
<span class="field-validation-valid" data-valmsg-for="Url" data-valmsg-replace="true"></span>
<br />

 


Trích nguồn từ: (https://www.tektutorialshub.com/)

Lên trên