طراحی ValidationAttribute دلخواه و هماهنگ سازی آن با ASP.NET MVC
نویسنده: صابر فتح الهی
تاریخ: ۱۳۹۲/۰۷/۲۱ ۱۵:۳۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
using System;
public class SampleModel
{
public DateTime StartDate { get; set; }
public string Data { get; set; }
public int Id { get; set; }
}
using System;
using System.ComponentModel.DataAnnotations;
public class SampleModel
{
[Required(ErrorMessage = "Start date is required")]
public DateTime StartDate { get; set; }
[Required(ErrorMessage = "Data is required")]
public string Data { get; set; }
public int Id { get; set; }
} @model SampleModel
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<section>
<header>
<h3>SampleModel</h3>
</header>
@Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" })
@using (Html.BeginForm("SaveData", "Sample", FormMethod.Post))
{
<p>
@Html.LabelFor(x => x.StartDate)
@Html.TextBoxFor(x => x.StartDate)
@Html.ValidationMessageFor(x => x.StartDate)
</p>
<p>
@Html.LabelFor(x => x.Data)
@Html.TextBoxFor(x => x.Data)
@Html.ValidationMessageFor(x => x.Data)
</p>
<input type="submit" value="Save"/>
}
</section> public class SampleController : Controller
{
//
// GET: /Sample/
public ActionResult Index()
{
return View();
}
public ActionResult SaveData(SampleModel item)
{
if (ModelState.IsValid)
{
//save data
}
else
{
ModelState.AddModelError("","لطفا خطاهای زیر را برطرف نمایید");
RedirectToAction("Index", item);
}
return View("Index");
}
}
تا اینجای کار روال عادی همیشگی است. اما برای طراحی Attribute دلخواه جهت اعتبار سنجی (مثلا برای مجبور کردن کاربر به وارد کردن یک فیلد با پیام دلخواه و زبان دلخواه) باید یک کلاس جدید تعریف کرده و از کلاس RequiredAttribute ارث ببرد. در پارامتر ورودی این کلاس جهت کار با Resourceهای ثابت در نظر گرفته شده است اما برای اینکه فیلد دلخواه را از دیتابیس بخواند این روش جوابگو نیست. برای انجام آن باید کلاس RequiredAttribute بازنویسی شود.
کلاس طراحی شده باید به صورت زیر باشد:
public class VegaRequiredAttribute : RequiredAttribute, IClientValidatable
{
#region Fields (2)
private readonly string _resourceId;
private String _resourceString = String.Empty;
#endregion Fields
#region Constructors (1)
public VegaRequiredAttribute(string resourceId)
{
_resourceId = resourceId;
ErrorMessage = _resourceId;
AllowEmptyStrings = true;
}
#endregion Constructors
#region Properties (1)
public new String ErrorMessage
{
get { return _resourceString; }
set { _resourceString = GetMessageFromResource(value); }
}
#endregion Properties
#region Methods (2)
// Public Methods (1)
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = GetMessageFromResource(_resourceId),
ValidationType = "required"
};
}
// Private Methods (1)
private string GetMessageFromResource(string resourceId)
{
var errorMessage = HttpContext.GetGlobalResourceObject(_resourceId, "Yes") as string;
return errorMessage ?? ErrorMessage;
}
#endregion Methods
} HttpContext.GetGlobalResourceObject(_resourceId, "Yes") as string;
که عنوان کلید Resource را از سازنده کلاس گرفته (کد اقای یوسف نژاد) رشته معادل آن را از دیتابیس بازیابی میکند.
2- ارث بری از اینترفیس IClientValidatable، در صورتی که از این اینترفیس ارث بری نداشته باشیم. Validator طراحی شده در طرف کلاینت کار نمیکند. بلکه کاربر با کلیک بروی دکمه مورد نظر دادهها را به سمت سرور ارسال میکند. در صورت وجود خطا در پست بک خطا نمایش داده خواهد شد. اما با ارث بری از این اینترفیس و پیاده سازی متد GetClientValidationRules میتوان تعریف کرد که در طرف کلاینت با استفاده از Unobtrusive jQuery پیام خطای مورد نظر به کنترل ورودی مورد نظر (مانند تکست باکس) اعمال میشود. مثلا در این مثال خصوصیت data-val-required به input هایی که قبلا در مدل ما Reqired تعریف شده اند اعمال میشود.
حال در مدل تعریف شده میتوان به جای Required میتوان از VegaRequiredAttribute مانند زیر استفاده کرد. (همراه با نام کلید مورد نظر در دیتابیس)
public class SampleModel
{
[VegaRequired("RequiredMessage")]
public DateTime StartDate { get; set; }
[VegaRequired("RequiredMessage")]
public string Data { get; set; }
public int Id { get; set; }
}
<script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> <appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings> <script type="text/javascript">
jQuery.validator.addMethod('required', function (value, element, params) {
if (value == null | value == "") {
return false;
} else {
return true;
}
}, '');
jQuery.validator.unobtrusive.adapters.add('required', {}, function (options) {
options.rules['required'] = true;
options.messages['required'] = options.message;
});
</script> public class LocalizedRequiredAttribute : RequiredAttribute, IClientValidatable
{
private readonly string _resourceKey;
public string SourceName { get; set; } = LocalizationSourceNames.Default;
public LocalizedRequiredAttribute(string resourceKey)
{
_resourceKey = resourceKey;
}
public override string FormatErrorMessage(string name)
{
return LocalizationHelper.GetString(SourceName, _resourceKey);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRequiredRule(FormatErrorMessage(null));
}
}