Атрибуты проверки достоверности

156

Поддержка элементов управления проверкой достоверности на стороне клиента работает за счет создания элементов, которые имеют специальные атрибуты. В качестве примера ниже показан 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> к веб-формам. Атрибуты проверки достоверности перечислены в таблице ниже:

Атрибуты, поддерживаемые библиотекой jQuery Validation
Атрибут Описание
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 осуществляется с помощью атрибутов, описанных в таблице ниже:

Атрибуты, предназначенные для конфигурирования специального элемента управления ValidationRepeater
Имя Описание
ItemType

В этом атрибуте должно быть указано имя класса ValidationRepeaterDataItem. Чтобы получить нужный результат для шаблона (который вскоре будет описан), в атрибуте ItemType должен быть задан класс, который содержит детали атрибута проверки достоверности. Если ItemType отсутствует, элемент управления не сможет корректно работать

ModelType

Этот атрибут используется для указания имени класса модели, для которого необходимо генерировать атрибуты проверки достоверности HTML

Properties

Этот атрибут используется для указания свойств класса модели, которые будут обрабатываться. Свойства должны разделяться запятыми

Элемент управления ValidationRepeater поддерживает шаблон по имени PropertyTemplate, экземпляр которого создается для каждого свойства, указанного в конфигурационном атрибуте Properties. Внутри шаблона с помощью фрагментов кода привязки данных Item можно ссылаться на два значения, представленные в таблице ниже:

Свойства Item, доступные в PropertyTemplate
Имя Описание
PropertyName

Имя текущего свойства

ValidationAttributes

Одиночная строка, содержащая атрибуты HTML-элементов, которые требуются для выполнения ненавязчивой проверки достоверности на основе атрибутов проверки C#, примененных к текущему свойству в классе модели

В результате мы получаем преимущество работы с атрибутами ненавязчивой проверки достоверности, которые были описаны в предыдущем разделе, без необходимости в дублировании политики проверки в классе модели и внутри веб-формы. Созданный элемент управления ValidationRepeater несколько груб и не поддерживает всего диапазона атрибутов проверки, однако он демонстрирует возможность комбинирования разнообразных приемов, описанных в предшествующих статьях, с целью обхода некоторых ограничений, присущих проверке достоверности клиентской стороны ASP.NET.

Пройди тесты
Лучший чат для C# программистов