Chuyển đến nội dung chính

Custom ASP.NET Validation

Trong bài viết này, mình sẽ hướng dẫn các bạn tùy biến Validator trong ASP.NET MVC và cách viết 1 validator ở phía Client side.

Custom ASP.NET Validator

Trong ví dụ này, chúng ta sẽ viết 1 custom validator để kiếm tra giá trị của 1 property này sẽ không bằng giá trị của 1 property khác.
Trong form này, bạn sẽ kiểm tra 2 field UserName và Password. Nếu giá trị Password bằng với UserName, sẽ xuất thông báo lỗi: Password cannot be the same as Username


Bạn tạo 1 class mới tên NotEqualToAttribute

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class NotEqualToAttribute : ValidationAttribute
{
    private const string DefaultErrorMessage = "{0} cannot be the same as {1}.";

    public string OtherProperty { get; private set; }

    public NotEqualToAttribute(string otherProperty)
      : base(DefaultErrorMessage)
    {
        if (string.IsNullOrEmpty(otherProperty))
        {
            throw new ArgumentNullException("otherProperty");
        }

        OtherProperty = otherProperty;
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(ErrorMessageString, name, OtherProperty);
    }

    protected override ValidationResult IsValid(object value,
                          ValidationContext validationContext)
    {
        if (value != null)
        {
            var otherProperty = validationContext.ObjectInstance.GetType()
                               .GetProperty(OtherProperty);

            var otherPropertyValue = otherProperty
                          .GetValue(validationContext.ObjectInstance, null);

            if (value.Equals(otherPropertyValue))
            {
                return new ValidationResult(
                  FormatErrorMessage(validationContext.DisplayName));
            }
        }

        return ValidationResult.Success;
    }
}
ValidationAttribute: là 1 abstract class, chứa phương thức virtual là IsValid.
IsValid: chứa 2 tham số đầu vào: value và validationContext. ValidationContext là đối tượng context mà validation cần thực hiện kiểm tra. Kết quả trả về là ValidationResult.Success hoặc đối tượng ValidationResult(errorMessage).
Đoạn code validationContext.ObjectInstance sẽ trả về đối tượng cần validate. Thông qua đối tượng trả về, bạn có thể lấy được value của các property khác. Để lấy được value của property khác, bạn thực hiện đoạn code reflection:
otherProperty.GetValue(validationContext.ObjectInstance, null);
Đoạn code: [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] báo cho .NET biết attribute này chỉ sử dụng cho property, và chỉ tạo được 1 instance trên mỗi property.
Constructor: NotEqualToAttribute(string otherProperty) cho phép bạn khởi tạo giá trị cho other property
Bạn mở website lên, nhập user name và mật khẩu cùng 1 giá trị sẽ thấy thông báo lỗi:
 

Client side Validation

Ví dụ 1:

Để kiểm tra model ở phía browser, bạn cần thêm jquery và jquery validatioin vào file html và đảm bảo 2 key ClientValidationEnabled và UnobtrusiveJavaScriptEnabled luôn được set bằng true.

<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
Trong ví dụ này, chúng ta sẽ làm 1 khung tìm kiếm, với điều kiện là không cho phép nhập dấu sao (*) ở đầu keyword
Dựa vào ví dụ trước đó, bạn sẽ tạo class NoAsteriskAtBegining

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class NoAsteriskAtBegining : ValidationAttribute
{
    private const string DefaultErrorMessage = "Asterisk starting is not allow";

    public NoAsteriskAtBegining() : base(DefaultErrorMessage)
    {

    }

    protected override ValidationResult IsValid(object value,
                          ValidationContext validationContext)
    {
        if (value != null)
        {
            var propValue = value.ToString();
            if(propValue.Length > 0)
            {
                if(propValue.StartsWith("*"))
                    return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
            }
            return ValidationResult.Success;                
        }

        return ValidationResult.Success;
    }
}
ở Controller, bạn thêm đoạn code sau:

public class SearchController : Controller
{
    // GET: Search
    public ActionResult Index()
    {
        return View(new SearchModel());
    }

    [HttpPost]
    public ActionResult Index(SearchModel searchModel, FormCollection form)
    {
        if (ModelState.IsValid)
        {
            //Do something
        }
        return View("Index");
    }
}
Ở View:

@using ModelValidationSamples.Models;
@model SearchModel
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("Index", "Search", FormMethod.Post, new { @id = "searchFormId", @class = "form-horizontal", role = "form" }))
{
    @Html.ValidationSummary(true)
<fieldset>
    <div class="form-group">
        @Html.LabelFor(m => m.Keyword, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Keyword, new { @class = "form-control" })
            @Html.ValidationMessageFor(m => m.Keyword, "", new { @class = "text-danger" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input class="btn btn-primary" type="submit" value="Ok" />
            <input class="btn btn-default" type="button" value="Cancel" />
        </div>
    </div>
</fieldset>
}
Chạy chương trình, nhập *, rồi Enter, bạn sẽ thấy kết quả như sau:

Muốn chạy được ở phía client, thì class NoAsteriskAtBegining phải kế thừa interface IClientValidatable và implement method GetClientValidationRules:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class NoAsteriskAtBegining : ValidationAttribute, IClientValidatable
{
 private const string DefaultErrorMessage = "Asterisk starting is not allow";

 public NoAsteriskAtBegining() : base(DefaultErrorMessage)
 {

 }

 public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
 {
  var clientValidationRule = new ModelClientValidationRule()
  {
   ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
   ValidationType = "custom"
  };
  //var OtherProperty = "";
  //clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty);
  yield return clientValidationRule;
 }

 protected override ValidationResult IsValid(object value, ValidationContext validationContext)
 {
  if (value != null)
  {
   var propValue = value.ToString();
   if(propValue.Length > 0)
   {
    if(propValue.StartsWith("*"))
     return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
   }
   return ValidationResult.Success;                
  }

  return ValidationResult.Success;
 }
}
ValidationType: bạn chỉ ra loại dữ liệu cần kiểm tra. Lưu ý đây phải là tên duy nhất. clientValidationRule.ValidationParameters.Add: Khai bao input parameter để sử dụng trong jquery Validation Để kiểm tra dữ liệu trên trang Html, bạn cần tạo thêm hàm validation và liên kết với metadata bằng jquery adapter. Trong file cshtml:

(function ($) {                        
  $.validator.unobtrusive.adapters.add("custom", function (options) {
   options.rules['custom'] = {};
   options.messages['custom'] = options.message;
  });
  $.validator.addMethod("custom", function (value, element, params) {                
   if (value != undefined && value.length > 0) {
    if (value[0] === '*') {
     return false;
    }
   }
   return true;                
  });
  

 }(jQuery));
Hàm add trong jquery unobtrsive:

adapters.add = function (adapterName, params, fn) {
 /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation.
 /// The name of the adapter to be added. This matches the name used
 /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).
 /// [Optional] An array of parameter names (strings) that will
 /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and
 /// mmmm is the parameter name).
 /// The function to call, which adapts the values from the HTML
 /// attributes into jQuery Validate rules and/or messages.
 /// 
 if (!fn) {  // Called with no params, just a function
  fn = params;
  params = [];
 }
 this.push({ name: adapterName, params: params, adapt: fn });
 return this;
};
Do hàm kiểm tra * không có parameter, nên bạn cần thêm 1 function Javascript. Để tìm hiểu thêm về function đó, bạn đi tới ví dụ tiếp theo.

Ví dụ 2: Truyền input parameter

Trong ví dụ này, bạn cần truyền parameter từ html vào function trong Javascript
Quay trở lại form Register, bạn sửa lại đoạn code trong file class NotEqualTo.cs

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class NotEqualToAttribute : ValidationAttribute, IClientValidatable
{
 private const string DefaultErrorMessage = "{0} cannot be the same as {1}.";

 public string OtherProperty { get; private set; }

 public NotEqualToAttribute(string otherProperty)
   : base(DefaultErrorMessage)
 {
  if (string.IsNullOrEmpty(otherProperty))
  {
   throw new ArgumentNullException("otherProperty");
  }

  OtherProperty = otherProperty;
 }

 public override string FormatErrorMessage(string name)
 {
  return string.Format(ErrorMessageString, name, OtherProperty);
 }

 protected override ValidationResult IsValid(object value, ValidationContext validationContext)
 {
  if (value != null)
  {
   var otherProperty = validationContext.ObjectInstance.GetType()
          .GetProperty(OtherProperty);

   var otherPropertyValue = otherProperty
        .GetValue(validationContext.ObjectInstance, null);

   if (value.Equals(otherPropertyValue))
   {
    return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));                    
   }
  }

  return ValidationResult.Success;
 }

 public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
 {
  var clientValidationRule = new ModelClientValidationRule()
  {
   ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
   ValidationType = "notequalto"
  };

  clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty);

  return new[] { clientValidationRule };
 }
}
Bạn mở file Index.cshtml, cập nhật lại đoạn Javascript:

(function ($) {
 $.validator.addMethod("notequalto", function (value, element, params) {
  if (!this.optional(element)) {
   //var otherProp = $('#' + params)
   //return (otherProp.val() != value);
   return $(params).val() != value;
  }
  return true;
 });
 //$.validator.unobtrusive.adapters.addSingleVal("notequalto", "otherproperty");
 $.validator.unobtrusive.adapters.add("notequalto", ["otherproperty"], function (options) {
  options.rules["notequalto"] = "#" + options.params.otherproperty;
  options.messages["notequalto"] = options.message;
 });
}(jQuery));
Giải thích options.rules["notequalto"] = "#" + options.params.otherproperty: Thêm dấu '#' vào trước tên property. Khi bạn gọi lấy giá trị của otherproperty, bạn viết $(params).val() thay vì viết $('#' + params).val().
Download source code: MediaFire

Tham khảo

https://thewayofcode.wordpress.com/2012/01/18/custom-unobtrusive-jquery-validation-with-data-annotations-in-mvc-3/
http://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-2
https://www.itorian.com/2013/08/enabling-client-side-validation-on.html
https://thewayofcode.wordpress.com/2012/01/18/custom-unobtrusive-jquery-validation-with-data-annotations-in-mvc-3/

Nhận xét

  1. Nếu bạn không mở được example thì mở file *.csprj, thêm vào dòng code sau:






    Trả lờiXóa
    Trả lời
    1. lt;Target Name="CopyRoslynFiles" AfterTargets="AfterBuild" Condition="!$(Disable_CopyWebApplication) And '$(OutDir)' != '$(OutputPath)'">
      lt;ItemGroup>
      lt;RoslynFiles Include="$(CscToolPath)\*" />
      lt;/ItemGroup>
      lt;MakeDir Directories="$(WebProjectOutputDir)\bin\roslyn" />
      lt;Copy SourceFiles="@(RoslynFiles)" DestinationFolder="$(WebProjectOutputDir)\bin\roslyn" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" />
      lt;/Target>

      Xóa

Đăng nhận xét

Bài đăng phổ biến từ blog này

[ASP.NET MVC] Authentication và Authorize

Một trong những vấn đề bảo mật cơ bản nhất là đảm bảo những người dùng hợp lệ truy cập vào hệ thống. ASP.NET đưa ra 2 khái niệm: Authentication và Authorize Authentication xác nhận bạn là ai. Ví dụ: Bạn có thể đăng nhập vào hệ thống bằng username và password hoặc bằng ssh. Authorization xác nhận những gì bạn có thể làm. Ví dụ: Bạn được phép truy cập vào website, đăng thông tin lên diễn đàn nhưng bạn không được phép truy cập vào trang mod và admin.

ASP.NET MVC: Cơ bản về Validation

Validation (chứng thực) là một tính năng quan trọng trong ASP.NET MVC và được phát triển trong một thời gian dài. Validation vắng mặt trong phiên bản đầu tiên của asp.net mvc và thật khó để tích hợp 1 framework validation của một bên thứ 3 vì không có khả năng mở rộng. ASP.NET MVC2 đã hỗ trợ framework validation do Microsoft phát triển, tên là Data Annotations. Và trong phiên bản 3, framework validation đã hỗ trợ tốt hơn việc xác thực phía máy khách, và đây là một xu hướng của việc phát triển ứng dụng web ngày nay.

Tổng hợp một số kiến thức lập trình về Amibroker

Giới thiệu về Amibroker Amibroker theo developer Tomasz Janeczko được xây dựng dựa trên ngôn ngữ C. Vì vậy bộ code Amibroker Formula Language sử dụng có syntax khá tương đồng với C, ví dụ như câu lệnh #include để import hay cách gói các object, hàm trong các block {} và kết thúc câu lệnh bằng dấu “;”. AFL trong Amibroker là ngôn ngữ xử lý mảng (an array processing language). Nó hoạt động dựa trên các mảng (các dòng/vector) số liệu, khá giống với cách hoạt động của spreadsheet trên excel.