Атрибуты проверки достоверности
156ASP.NET --- ASP.NET Web Forms 4.5 --- Атрибуты проверки достоверности
Поддержка элементов управления проверкой достоверности на стороне клиента работает за счет создания элементов, которые имеют специальные атрибуты. В качестве примера ниже показан HTML-элемент, сгенерированный элементом управления RequiredFieldValidator, который был применен к элементу <input> для свойства Name в предыдущей статье:
... <span data-val-controltovalidate="Name" data-val-errormessage="Имя должно быть указано" data-val="true" data-val-evaluationfunction="RequiredFieldValidatorEvaluateIsValid" data-val-initialvalue="" id="ctl03" class="error" style="visibility:hidden;">*</span> ...
Элементы управления проверкой достоверности генерируют элементы с атрибутами данных, которые потребляются кодом JavaScript из файла jquery.validate.unobtrusive.js (добавленного пакетом Microsoft jQuery Unobtrusive Validation) и используются кодом внутри файла jquery.validate.js (добавленного пакетом jQuery.Validation); оба файла были установлены с помощью диспетчера пакетов NuGet ранее.
Чтобы улучшить поведение проверки достоверности клиентской стороны, можно работать напрямую с указанными библиотеками JavaScript и пропускать промежуточный шаг, связанный с генерацией конфигурационных элементов посредством элементов управления проверкой достоверности. В примере ниже приведен модифицированный контент веб-формы CreateGame.aspx, в котором осуществляется работа непосредственно с библиотеками JavaScript:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CreateGame.aspx.cs" Inherits="ClientDev.CreateGame" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title></title>
<style>
...
.input-validation-error { border: medium solid red;}
</style>
<%: System.Web.Optimization.Scripts.Render("~/bundle/validation") %>
</head>
<body>
<form id="form1" runat="server">
<div id="errorSummary" data-valmsg-summary="true" class="error">
<ul><li style="display:none"></li></ul>
<asp:ValidationSummary runat="server" CssClass="error" />
</div>
<table>
<tr>
<td>Название:</td>
<td><input id="Name" runat="server"
data-val="true" data-val-required="Имя должно быть указано"
data-val-length="Имя должно содержать от 5 до 20 символов."
data-val-length-min="5" data-val-length-max="20"/>
</td>
</tr>
<tr>
<td>Категория:</td>
<td><input id="Category" runat="server"
data-val="true" data-val-required="Категория должна быть указана" /></td>
</tr>
<tr>
<td>Цена:</td>
<td><input id="Price" runat="server"
data-val="true" data-val-required="Цена должна быть указана"
data-val-range="Цена должна находиться в пределах от 1 до 10000."
data-val-range-min="1" data-val-range-max="10000" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Добавить игру" runat="server"/></td>
</tr>
<tr><th>ID</th><th>Название</th><th>Категория</th><th>Цена</th></tr>
<asp:Repeater runat="server"
...
</asp:Repeater>
</table>
</form>
</body>
</html>
Первое изменение связано с определением стиля CSS для класса .input-validation-error. Этот класс добавляется к элементам <input>, которые не прошли проверку достоверности:
.input-validation-error { border: medium solid red;}
Мы установили рамку красного цвета, которая будет выделять проблемные значения для пользователя. Следующее изменение касается определения контейнера для элемента управления ValidationSummary, к которому применяется атрибут data-valmsg-summary:
... <div id="errorSummary" data-valmsg-summary="true" class="error"> <ul><li style="display:none"></li></ul> <asp:ValidationSummary runat="server" CssClass="error" /> </div> ...
Данный атрибут сообщает библиотеке проверки достоверности о том, что сводка по ошибкам должна быть вставлена в виде группы элементов <li> внутри элемента <ul>. Это никак не влияет на элемент управления ValidationSummary, который по-прежнему будет использоваться для отображения ошибок проверки достоверности серверной стороны. Помещение элемента управления ValidationSummary внутрь элемента <div> позволяет обеспечить единообразную стилизацию и согласованные сообщения об ошибках проверки достоверности.
Определение политики проверки достоверности
Понятие "ненавязчивая", используемое в отношении проверки достоверности форм, означает конфигурирование проверки путем применения атрибутов данных к элементам <input> без написания какого-либо кода JavaScript. Это обеспечивается библиотекой Microsoft. Она позволяет использовать исключительно гибкую и адаптируемую библиотеку jQuery Validation, не добавляя специальные элементы <script> к веб-формам. Атрибуты проверки достоверности перечислены в таблице ниже:
Атрибут | Описание |
---|---|
data-val | Когда этот атрибут установлен в true, для элемента <input> включается проверка достоверности |
data-val-required | Значение этого атрибута отображается в качестве сообщения об ошибке, если пользователь не предоставил значения |
data-val-length | Значение этого атрибута отображается в качестве сообщения об ошибке, когда длина значения в элементе <input> выходит за пределы диапазона, установленного с помощью атрибутов data-val-length-min и data-val-length-max |
data-val-range | Значение этого атрибута отображается в качестве сообщения об ошибке, когда значение в элементе <input> выходит за пределы диапазона, установленного с помощью атрибутов data-val-range-min и data-val-range-max |
data-val-regex | Значение этого атрибута отображается в качестве сообщения об ошибке, когда значение в элементе <input> не соответствует регулярному выражению, определенному в атрибуте data-val-regex-pattern |
data-val-digits | Значение этого атрибута отображается в качестве сообщения об ошибке, когда значение в элементе <input> содержит любые нецифровые символы |
data-val-number | Значение этого атрибута отображается в качестве сообщения об ошибке, когда значение в элементе <input> не может быть преобразовано в число. Это отличается от атрибута data-val-digits, т.к. разрешено вводить десятичную точку и символ минуса для указания отрицательного значения |
Благодаря этой таблице, можно понять, как была сконфигурирована проверка достоверности клиентской стороны внутри веб-формы. Для получения требуемого результата мы установили атрибут data-val в true во всех элементах <input>, а также применили атрибут data-val-required вместе с data-val-range и data-val-length.
Запустите приложение, запросите веб-форму CreateGame.aspx и щелкните на кнопке "Добавить игру", ничего не вводя в элементах <input>. Все элементы <input> будут выделены, указывая на наличие проблем, и отобразятся сообщения клиентской стороны о том, что значения являются обязательными (действие атрибута data-val-required). Чтобы увидеть результат работы других атрибутов проверки, введите строку длиной менее 5 символов в поле "Имя" и любую строку в поле "Цена", после чего снова щелкните на кнопке:
Устранение дублирования политики проверки достоверности
Нам нравится работать с атрибутами ненавязчивой проверки, но по-прежнему остается проблема с размещением политики проверки достоверности сразу в двух местах. Следующий шаг заключается в применении приемов, связанных со специальными элементами управления и шаблонами данных, для генерации HTML-элементов, которые имеют атрибуты проверки достоверности, выведенные из класса модели. С этой целью мы создаем файл класса веб-элемента управления под названием ValidationRepeater.cs, содержимое которого показано в примере ниже:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace ClientDev
{
public class ValidationRepeater : DataBoundControl, INamingContainer
{
[TemplateContainer(typeof(ValidationRepeaterTemplateItem))]
public ITemplate PropertyTemplate { get; set; }
public string Properties { get; set; }
public string ModelType { get; set; }
private bool IsAttrDefined(Type attrType, Type targetType, string propName)
{
return Attribute.IsDefined(targetType.GetProperty(propName), attrType);
}
protected override void RenderContents(HtmlTextWriter writer)
{
Type targetType = Type.GetType(ModelType);
string[] propertyNames = Properties.Split(',');
foreach (string propRaw in propertyNames)
{
string property = propRaw.Trim();
Dictionary<string, object> valAttribs = new Dictionary<string, object>();
valAttribs.Add("data-val", "true");
if (Context.Request.Form[property] != null)
{
valAttribs.Add("value", Context.Request.Form[property]);
}
if (IsAttrDefined(typeof(RequiredAttribute), targetType, property))
{
valAttribs.Add("data-val-required",
string.Format("Provide a value for {0}", property));
}
if (IsAttrDefined(typeof(StringLengthAttribute), targetType, property))
{
object[] attrs = targetType.GetProperty(property)
.GetCustomAttributes(typeof(StringLengthAttribute), false);
if (attrs.Length > 0)
{
StringLengthAttribute attr = (StringLengthAttribute)attrs[0];
valAttribs.Add("data-val-length", attr.ErrorMessage ??
string.Format("{0} must be {1}-{2} characters",
property, attr.MinimumLength, attr.MaximumLength));
valAttribs.Add("data-val-length-min", attr.MinimumLength);
valAttribs.Add("data-val-length-max", attr.MaximumLength);
}
}
if (IsAttrDefined(typeof(RangeAttribute), targetType, property))
{
object[] attrs = targetType.GetProperty(property)
.GetCustomAttributes(typeof(RangeAttribute), false);
if (attrs.Length > 0)
{
RangeAttribute attr = (RangeAttribute)attrs[0];
valAttribs.Add("data-val-range", attr.ErrorMessage ??
string.Format("{0} must be {1}-{2} ",
property, attr.Minimum, attr.Maximum));
valAttribs.Add("data-val-range-min", attr.Minimum);
valAttribs.Add("data-val-range-max", attr.Maximum);
}
}
List<string> attrList = new List<string>();
foreach (string key in valAttribs.Keys)
{
attrList.Add(string.Format("{0}='{1}'", key, valAttribs[key]));
}
ValidationRepeaterTemplateItem elem
= new ValidationRepeaterTemplateItem
{
DataItem = new ValidationRepeaterDataItem
{
PropertyName = property,
ValidationAttributes = string.Join(" ", attrList.ToArray())
}
};
PropertyTemplate.InstantiateIn(elem);
elem.DataBind();
elem.RenderControl(writer);
}
}
}
public class ValidationRepeaterDataItem
{
public string PropertyName { get; set; }
public string ValidationAttributes { get; set; }
}
public class ValidationRepeaterTemplateItem : Control, IDataItemContainer
{
public object DataItem { get; set; }
public int DataItemIndex { get; set; }
public int DisplayIndex { get; set; }
}
}
Этот элемент управления использует рефлексию для поиска атрибутов проверки достоверности Required, StringLength и Range в классе модели и генерирует HTML-атрибуты, требуемые для выполнения ненавязчивой проверки достоверности клиентской стороны. В примере ниже демонстрируется применение этого элемента управления внутри веб-формы CreateGame.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CreateGame.aspx.cs" Inherits="ClientDev.CreateGame" %>
<%@ Register TagPrefix="control" Assembly="ClientDev" Namespace="ClientDev" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title></title>
<style>
...
</style>
<%: System.Web.Optimization.Scripts.Render("~/bundle/validation") %>
</head>
<body>
<form id="form1" runat="server">
<div id="errorSummary" data-valmsg-summary="true" class="error">
<ul><li style="display:none"></li></ul>
<asp:ValidationSummary runat="server" CssClass="error" />
</div>
<table>
<control:ValidationRepeater runat="server"
ItemType="ClientDev.ValidationRepeaterDataItem"
ModelType ="ClientDev.Models.Game"
Properties="Name, Category, Price" >
<PropertyTemplate>
<tr>
<td><%# Item.PropertyName %></td>
<td>
<input id="<%# Item.PropertyName %>"
name="<%# Item.PropertyName %>"
<%# Item.ValidationAttributes %> />
</td>
</tr>
</PropertyTemplate>
</control:ValidationRepeater>
<tr>
<td colspan="2"><input type="submit" value="Добавить игру" runat="server"/></td>
</tr>
...
</table>
</form>
</body>
</html>
Элементу управления ValidationRepeater необходима возможность поддержки шаблонов, поэтому для достижения нужных результатов пришлось прибегнуть к ненормальному использованию некоторых характеристик элементов управления данными и шаблонов. Конфигурирование ValidationRepeater осуществляется с помощью атрибутов, описанных в таблице ниже:
Имя | Описание |
---|---|
ItemType | В этом атрибуте должно быть указано имя класса ValidationRepeaterDataItem. Чтобы получить нужный результат для шаблона (который вскоре будет описан), в атрибуте ItemType должен быть задан класс, который содержит детали атрибута проверки достоверности. Если ItemType отсутствует, элемент управления не сможет корректно работать |
ModelType | Этот атрибут используется для указания имени класса модели, для которого необходимо генерировать атрибуты проверки достоверности HTML |
Properties | Этот атрибут используется для указания свойств класса модели, которые будут обрабатываться. Свойства должны разделяться запятыми |
Элемент управления ValidationRepeater поддерживает шаблон по имени PropertyTemplate, экземпляр которого создается для каждого свойства, указанного в конфигурационном атрибуте Properties. Внутри шаблона с помощью фрагментов кода привязки данных Item можно ссылаться на два значения, представленные в таблице ниже:
Имя | Описание |
---|---|
PropertyName | Имя текущего свойства |
ValidationAttributes | Одиночная строка, содержащая атрибуты HTML-элементов, которые требуются для выполнения ненавязчивой проверки достоверности на основе атрибутов проверки C#, примененных к текущему свойству в классе модели |
В результате мы получаем преимущество работы с атрибутами ненавязчивой проверки достоверности, которые были описаны в предыдущем разделе, без необходимости в дублировании политики проверки в классе модели и внутри веб-формы. Созданный элемент управления ValidationRepeater несколько груб и не поддерживает всего диапазона атрибутов проверки, однако он демонстрирует возможность комбинирования разнообразных приемов, описанных в предшествующих статьях, с целью обхода некоторых ограничений, присущих проверке достоверности клиентской стороны ASP.NET.