Часть 14 – Удаленная проверка данных

Осталось реализовать предварительную проверку на стороне клиента для одного свойства – Login. Её особенность в необходимости осуществлять запрос на сервер при каждом изменении пользователем значения в соответствующем поле формы ввода. Можно конечно разработать необходимый функционал самостоятельно и, по сути, заново изобрести колесо. Или воспользоваться возможностями ASP.NET MVC 3.

Проверка данных при помощи атрибута [Remote]

Как можно заметить, сценарий рассматриваемой проверки достаточно однотипный. Необходимо переслать проверяемое значение на сервер, где оно будет проверено, и получить ответ, является ли оно корректным.

В ASP.NET MVC 3 для реализации такого сценария предусмотрен атрибут [Remote], расположенный в пространстве имен System.Web.Mvc. Рассмотрим принципы его использования.

Как и любой другой, данный атрибут вначале необходимо присвоить выбранному свойству в одном из классов Модели. Это задействует в Представлениях механизм, который будет отправлять запросы на сервер при изменении значения соответствующего поля формы ввода.

В качества параметров конструктора [Remote] выступает название Действия, выполняющего необходимую проверку, и имя Контроллера в котором оно расположено. Дополнительные настройки осуществляются при помощи свойств:

  • HttpMethod – указывает тип запроса: POST или GET. Последний используется по умолчанию.
  • AdditionalFields – позволяет дополнительно передавать в Контроллер значения из указанных полей формы ввода. По умолчанию передается только значение поля, связанного с контролируемым свойством Модели.

После установки атрибута необходимо создать Действие. При этом стоит отметить, что согласно идеологии шаблона MVC, Контроллер не должен выполнять непосредственную проверку полученного значения. Его задача состоит только в определении необходимого метода Модели и переадресации ей проверяемого значения.

Результатом проверки является значение true, если она прошла успешна. В противном случае должно быть возвращено сообщение об ошибке или false, чтобы использовать текст, указанный при установке атрибута. Ответ пересылается Контроллером в клиентскую часть, используя формат Json. Для упрощения этой задачи можно воспользоваться методом Json(). Обработка результата и, при необходимости, вывод сообщения на стороне клиента осуществляется функционалом ASP.NET MVC 3 самостоятельно.

Таким образом, для реализации удаленной проверки данных необходимо выполнить только два действия: назначить атрибут свойству и создать метод проверки значения.

Реализация удаленной проверки свойства Login

Удаленная проверка будет добавлена в NewUserProfileModel. Как и в случае с серверной реализацией, причина заключена в её необходимости только при создании нового профиля.

Для этого в базовом классе UserProfileModel обозначим свойство Login как виртуальное. Это позволит переопределить его в NewUserProfileModel и добавить новый атрибут, унаследовав заданные ранее.

Действие, ответственное за проверку, назовем "IsLoginExists". Кроме того, будем использовать запрос типа POST и уже существующей текст сообщения об ошибках.

namespace MVCDemo.Models
{
    using System.ComponentModel.DataAnnotations;
    using MVCDemo.Attributes.Validation;
    using MVCDemo.Resources.Models;
    using MVCDemo.Resources.Shared;

    public class UserProfileModel
    {
        [Display(Name = "Login", ResourceType = typeof(UserProfileRes))]
        [DataType(DataType.Text)]
        [Required(ErrorMessageResourceName = "FieldIsRequired",
            ErrorMessageResourceType = typeof(ErrorsRes))]
        [StringLengthRange(3, 64,
            ErrorMessageResourceName = "InvalidStringLength",
            ErrorMessageResourceType = typeof(ErrorsRes))]
        public virtual string Login { get; set; }

        .........
    }
}
namespace MVCDemo.Models
{
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Web.Mvc;
    using MVCDemo.Attributes.Validation;
    using MVCDemo.Resources.Models;
    using MVCDemo.Resources.Shared;

    public class NewUserProfileModel : UserProfileModel, IValidatableObject
    {
        [Remote("IsLoginExists", "UserProfiles", HttpMethod = "POST",
            ErrorMessageResourceName = "LoginAlreadyExists",
            ErrorMessageResourceType = typeof(NewUserProfileRes))]
        public override string Login { get; set; }
    }
}

Перейдем к модификации Контроллера. В Модели уже существует метод для проверки уникальности значения имени входа. Поэтому достаточно просто переадресовать ему запрос и вернуть ответ.

namespace MVCDemo.Controllers
{
    using System.Web.Mvc;
    using MVCDemo.Models;

    public class UserProfilesController : Controller
    {
        .........

        // POST: /UserProfiles/IsLoginExists/
        [HttpPost]
        public JsonResult IsLoginExists(string login)
        { 
            var userRepository = new UserRepository();
            bool isExist = userRepository.IsLoginExists(login);
            return this.Json(!isExist);
        } 
    }
}

Запустим проект и убедимся в работоспособности сделанных изменений.

Давайте посмотрим, где расположились параметры, заданные для удаленной проверки. Для этого откроем HTML код страницы создания нового профиля:

<div class="editor-label">
    <label for="Login">Login</label>
</div>
<div class="editor-field">
    <input class="text-box single-line" data-val="true" 
data-val-remote="Login already exists, please choose another."
data-val-remote-additionalfields="*.Login" 
data-val-remote-type="POST" 
data-val-remote-url="/UserProfiles/IsLoginExists" 
data-val-required="You must enter a Login." data-val-strlenrange="Login length must be between 3 and 64." data-val-strlenrange-max="64" data-val-strlenrange-min="3" id="Login" name="Login" type="text" value="" />
    <span class="field-validation-valid" data-valmsg-for="Login" data-valmsg-replace="true"></span>
</div>

Как хорошо видно, они разместились в атрибутах соответствующего HTML тега, названия которых начинается с "data-val-remote". Здесь так же используется ненавязчивый JavaScript. Функционал самой проверки, как и для остальных атрибутов, возложен на jQuery Validation.

Реализация запланированных правил проверки свойств Модели завершена. Для этого были использованы возможности ASP.NET MVC, в том числе и представленные в 3 версии.


Исходный код проекта (C#, Visual Studio 2010): MVCDemo-Part14.zip (477 Kb)

Комментарии (11) -

Спасибо за статью, очень интересно.
К сожалению файл MVCDemo-Part14.zip загрузить не получилось

@ Roman: Исправил ссылку.

Андрей, спасибо за отличный цикл!
У меня вопрос по валидации моделей. Предположим, у меня, помимо приложения ASP.NET MVC, есть еще набор веб-сервисов. При этом и приложение, и веб-сервисы используют общую бизнес-логику. В этом случае более правильно вынести валидацию данных как раз в слой бизнес-логики, чтобы не дублировать ее во всех веб-сервисах. Но при этом, чтобы получить все "плюшки" MVC, продублировать валидацию в приложении, видимо, придется. Может быть есть какое-то изящное решение для такой ситуации?

Тут конечно нужно знать подробности самой модели и бизнес-логики. Поэтому ответ будет несколько общий. В данной ситуации можно посмотреть в сторону создания собственного провайдера правил проверки данных (буквально завтра будет часть посвященная этому). Это даст возможность установить соответствие между атрибутами DataAnnotations (как стандартными, так и вашими) и сценарием проверки в блоке бизнес-логики.

А ведь плохо, что модель знает что-то про контроллер. Например, явно указан какой метод какого контроллера вызывать в [Remote]

Согласен. Но вот как сделать по другому? Есть мысли?
Можно конечно навешивать [Remote] через провайдера проверок, но это тоже как-то не очень правильно выглядит. В этом случае, как ни крути, Модель должна знать куда ей обратиться за проверкой.

Вадим Мальцев 09.01.2012 3:09:47

Большое ��пасибо за отличный цикл статей!!!

У меня вопрос, валидация работает же и при создании и при редактировании формы?

При редактировании, даже если это поле не изменялось, выходит ошибка на не уникальность. Frown

Как ее избежать? Еще раз спасибо большое!!

@ Вадим Мальцев:
Спасибо. По заданному вопросу:

1) В Edit форме как правило такое поле отсутствует.

2) Но если такое поле может быть изменено (например, при редактирование имени входа), то можно передавать в RemoteAttribute еще и Id записи (устанавливается через свойство AdditionalFields). Тогда можно делать проверку по полю + Id (искать логин во всех записях, за исключением записи с указанным Id).

Вадим Мальцев 09.01.2012 16:03:06

@ Andrey:

Андрей, спасибо за ответ. Напишу немного подробнее. У меня на форме присутсвует поле: телефон. Он, есстественно, как и е-майл - у всех уникален. Он обычно вносится при регистрации человека. Но бывает и так, что человек может поменять симку и,соответсвенно, нужно заходить на форму Edit и менять телефон. Хотелось бы сделать так, чтобы в Edit тоже проверялся телефон на уникальность, но только в том случае, если это поле изменялось.

Сейчас форма Edit не дает ничего сохранить, даже если телефон не изменялся( а изменился к примеру e-mail), так как выходит ошибка, что такой телефон существует.

Вадим Мальцев 09.01.2012 18:54:48

@ Andrey:

Сначала не уловил сути, теперь понял Smile Сейчас попробую! Спасибо еще раз!

Pingbacks and trackbacks (3)+

Добавить комментарий