Проверка в корпоративных приложениях
Примечание.
Эта электронная книга была опубликована весной 2017 года и с тех пор не была обновлена. Есть много в книге, которая остается ценным, но некоторые из материалов устарели.
Любое приложение, которое принимает входные данные от пользователей, должно убедиться, что входные данные допустимы. Например, приложение может проверить входные данные, содержащие только символы в определенном диапазоне, определенную длину или соответствующий определенному формату. Без проверки пользователь может предоставить данные, которые приводят к сбою приложения. Проверка применяет бизнес-правила и предотвращает внедрение вредоносных данных злоумышленником.
В контексте шаблона Model-View-ViewModel (MVVM) модель представления или модель часто требуется для выполнения проверки данных и сигнала о любых ошибках проверки в представлении, чтобы пользователь смог исправить их. Мобильное приложение eShopOnContainers выполняет синхронную проверку свойств модели просмотра и уведомляет пользователя о любых ошибках проверки, выделите элемент управления, содержащий недопустимые данные, и отображая сообщения об ошибках, информирующие пользователя о том, почему данные недопустимы. На рисунке 6-1 показаны классы, участвующие в выполнении проверки в мобильном приложении eShopOnContainers.
Рис. 6-1. Классы проверки в мобильном приложении eShopOnContainers
Просмотр свойств модели, требующих проверки, имеет тип ValidatableObject<T>
, и каждый ValidatableObject<T>
экземпляр имеет правила проверки, добавленные в его Validations
свойство. Проверка вызывается из модели представления путем вызова Validate
метода экземпляра ValidatableObject<T>
, который извлекает правила проверки и выполняет их для ValidatableObject<T>
Value
свойства. Все ошибки проверки помещаются в Errors
свойство экземпляра ValidatableObject<T>
, а IsValid
свойство экземпляра ValidatableObject<T>
обновляется, чтобы указать, выполнена ли проверка успешно или не выполнена.
Уведомление об изменении свойств предоставляется классом ExtendedBindableObject
, поэтому Entry
элемент управления может привязаться к IsValid
свойству экземпляра ValidatableObject<T>
в классе модели представления, чтобы получать уведомление о том, является ли введенные данные допустимыми.
Указание правил проверки
Правила проверки задаются путем создания класса, наследуемого IValidationRule<T>
от интерфейса, который показан в следующем примере кода:
public interface IValidationRule<T>
{
string ValidationMessage { get; set; }
bool Check(T value);
}
Этот интерфейс указывает, что класс правил проверки должен предоставить boolean
Check
метод, используемый для выполнения обязательной проверки, и ValidationMessage
свойство, значение которого является сообщением об ошибке проверки, которое будет отображаться при сбое проверки.
В следующем примере кода показано IsNotNullOrEmptyRule<T>
правило проверки, которое используется для проверки имени пользователя и пароля, введенных пользователем LoginView
при использовании макетных служб в мобильном приложении eShopOnContainers:
public class IsNotNullOrEmptyRule<T> : IValidationRule<T>
{
public string ValidationMessage { get; set; }
public bool Check(T value)
{
if (value == null)
{
return false;
}
var str = value as string;
return !string.IsNullOrWhiteSpace(str);
}
}
Метод Check
возвращает boolean
значение, указывающее, является null
ли аргумент значения пустым или состоит только из символов пробелов.
Хотя и не используется мобильным приложением eShopOnContainers, в следующем примере кода показано правило проверки для проверки адресов электронной почты:
public class EmailRule<T> : IValidationRule<T>
{
public string ValidationMessage { get; set; }
public bool Check(T value)
{
if (value == null)
{
return false;
}
var str = value as string;
Regex regex = new Regex(@"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$");
Match match = regex.Match(str);
return match.Success;
}
}
Метод Check
возвращает значение, boolean
указывающее, является ли аргумент значения допустимым адресом электронной почты. Это достигается путем поиска аргумента значения для первого вхождения шаблона регулярного выражения, указанного в конструкторе Regex
. Можно ли определить, найден ли шаблон регулярного Match
выражения в входной строке, проверив значение свойства объекта Success
.
Примечание.
Проверка свойств иногда может включать зависимые свойства. Пример зависимых свойств заключается в том, что набор допустимых значений для свойства A зависит от конкретного значения, заданного в свойстве B. Чтобы проверить, является ли значение свойства A одним из допустимых значений, потребуется получить значение свойства B. Кроме того, при изменении значения свойства B необходимо будет пересмотреть свойство A.
Добавление правил проверки в свойство
В мобильном приложении eShopOnContainers свойства модели просмотра, требующие проверки, объявляются типом ValidatableObject<T>
, где T
является тип проверяемых данных. В следующем примере кода показан пример двух таких свойств:
public ValidatableObject<string> UserName
{
get
{
return _userName;
}
set
{
_userName = value;
RaisePropertyChanged(() => UserName);
}
}
public ValidatableObject<string> Password
{
get
{
return _password;
}
set
{
_password = value;
RaisePropertyChanged(() => Password);
}
}
Для выполнения проверки правила проверки должны быть добавлены в Validations
коллекцию каждого ValidatableObject<T>
экземпляра, как показано в следующем примере кода:
private void AddValidations()
{
_userName.Validations.Add(new IsNotNullOrEmptyRule<string>
{
ValidationMessage = "A username is required."
});
_password.Validations.Add(new IsNotNullOrEmptyRule<string>
{
ValidationMessage = "A password is required."
});
}
Этот метод добавляет IsNotNullOrEmptyRule<T>
правило проверки в коллекцию каждого ValidatableObject<T>
экземпляра, указывая значения свойства правила Validations
ValidationMessage
проверки, которое указывает сообщение об ошибке проверки, которое будет отображаться при сбое проверки.
Активация проверки
Подход проверки, используемый в мобильном приложении eShopOnContainers, может вручную активировать проверку свойства и автоматически активировать проверку при изменении свойства.
Активация проверки вручную
Проверку можно активировать вручную для свойства модели представления. Например, это происходит в мобильном приложении eShopOnContainers, когда пользователь нажимает кнопку "Вход " на сервере LoginView
, при использовании макетных служб. Делегат команды вызывает MockSignInAsync
метод в LoginViewModel
методе, который вызывает проверку путем выполнения Validate
метода, который показан в следующем примере кода:
private bool Validate()
{
bool isValidUser = ValidateUserName();
bool isValidPassword = ValidatePassword();
return isValidUser && isValidPassword;
}
private bool ValidateUserName()
{
return _userName.Validate();
}
private bool ValidatePassword()
{
return _password.Validate();
}
Метод Validate
выполняет проверку имени пользователя и пароля, введенных пользователем, LoginView
путем вызова метода Validate для каждого ValidatableObject<T>
экземпляра. В следующем примере кода показан метод Validate из ValidatableObject<T>
класса:
public bool Validate()
{
Errors.Clear();
IEnumerable<string> errors = _validations
.Where(v => !v.Check(Value))
.Select(v => v.ValidationMessage);
Errors = errors.ToList();
IsValid = !Errors.Any();
return this.IsValid;
}
Этот метод очищает Errors
коллекцию, а затем извлекает все правила проверки, добавленные в коллекцию объекта Validations
. Метод Check
для каждого полученного правила проверки выполняется, а ValidationMessage
значение свойства для любого правила проверки, которое не может проверить данные, добавляется в Errors
коллекцию экземпляра ValidatableObject<T>
. Наконец, свойство задано, и его значение возвращается вызывающему методу, указывая, IsValid
выполнена ли проверка успешно или завершилась ошибкой.
Активация проверки при изменении свойств
Проверка также может быть активирована всякий раз, когда изменяется связанное свойство. Например, когда двусторонняя привязка в наборах LoginView
UserName
или Password
свойстве активируется. В следующем примере кода показано, как это происходит:
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Behaviors>
<behaviors:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidateUserNameCommand}" />
</Entry.Behaviors>
...
</Entry>
Элемент Entry
управления привязывается к UserName.Value
свойству ValidatableObject<T>
экземпляра, а в коллекцию EventToCommandBehavior
элемента управления Behaviors
добавлен экземпляр. Это поведение выполняется в ответ на событие [TextChanged
], которое Entry
возникает ValidateUserNameCommand
при изменении текстаEntry
. В свою очередь делегат ValidateUserNameCommand
выполняет ValidateUserName
метод, который выполняет Validate
метод на экземпляре ValidatableObject<T>
. Таким образом, каждый раз, когда пользователь вводит символ в Entry
элементе управления для имени пользователя, выполняется проверка введенных данных.
Дополнительные сведения о поведении см. в разделе "Реализация поведения".
Отображение ошибок проверки
Мобильное приложение eShopOnContainers уведомляет пользователя о любых ошибках проверки, выделите элемент управления, содержащий недопустимые данные с красной линией, и отображая сообщение об ошибке, которое сообщает пользователю, почему данные недопустимы под элементом управления, содержащий недопустимые данные. При исправлении недопустимых данных строка изменяется на черное и сообщение об ошибке удаляется. На рисунке 6-2 показан элемент LoginView в мобильном приложении eShopOnContainers при наличии ошибок проверки.
Рис. 6-2. Отображение ошибок проверки во время входа
Выделение элемента управления, содержащего недопустимые данные
Присоединенное LineColorBehavior
поведение используется для выделения Entry
элементов управления, в которых произошли ошибки проверки. В следующем примере кода показано, как присоединенное LineColorBehavior
поведение присоединено к элементу Entry
управления:
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Style>
<OnPlatform x:TypeArguments="Style">
<On Platform="iOS, Android" Value="{StaticResource EntryStyle}" />
<On Platform="UWP" Value="{StaticResource UwpEntryStyle}" />
</OnPlatform>
</Entry.Style>
...
</Entry>
Элемент Entry
управления использует явный стиль, который показан в следующем примере кода:
<Style x:Key="EntryStyle"
TargetType="{x:Type Entry}">
...
<Setter Property="behaviors:LineColorBehavior.ApplyLineColor"
Value="True" />
<Setter Property="behaviors:LineColorBehavior.LineColor"
Value="{StaticResource BlackColor}" />
...
</Style>
Этот стиль задает ApplyLineColor
и LineColor
присоединенные свойства присоединенного LineColorBehavior
поведения в элементе Entry
управления. Дополнительные сведения о стилях см. в статье Стили .
Если задано значение присоединенного ApplyLineColor
свойства или изменяется, LineColorBehavior
присоединенное поведение выполняет OnApplyLineColorChanged
метод, который показан в следующем примере кода:
public static class LineColorBehavior
{
...
private static void OnApplyLineColorChanged(
BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as View;
if (view == null)
{
return;
}
bool hasLine = (bool)newValue;
if (hasLine)
{
view.Effects.Add(new EntryLineColorEffect());
}
else
{
var entryLineColorEffectToRemove =
view.Effects.FirstOrDefault(e => e is EntryLineColorEffect);
if (entryLineColorEffectToRemove != null)
{
view.Effects.Remove(entryLineColorEffectToRemove);
}
}
}
}
Параметры этого метода предоставляют экземпляр элемента управления, к которому присоединено поведение, а также старые и новые значения присоединенного ApplyLineColor
свойства. Класс EntryLineColorEffect
добавляется в коллекцию элемента управления Effects
, если ApplyLineColor
присоединенное свойство true
, в противном случае оно удаляется из коллекции элемента управления Effects
. Дополнительные сведения о поведении см. в разделе "Реализация поведения".
Подклассы EntryLineColorEffect
RoutingEffect
класса и показаны в следующем примере кода:
public class EntryLineColorEffect : RoutingEffect
{
public EntryLineColorEffect() : base("eShopOnContainers.EntryLineColorEffect")
{
}
}
Класс RoutingEffect
представляет независимый от платформы эффект, который обертывает внутренний эффект, зависящий от платформы. Это упрощает процесс удаления эффекта, так как отсутствует доступ времени компиляции к сведениям о типе для определяемого платформой эффекта. Вызывает EntryLineColorEffect
конструктор базового класса, передавая параметр, состоящий из объединения имени группы разрешения, и уникальный идентификатор, указанный в каждом классе эффектов для конкретной платформы.
В следующем примере кода показана eShopOnContainers.EntryLineColorEffect
реализация для iOS:
[assembly: ResolutionGroupName("eShopOnContainers")]
[assembly: ExportEffect(typeof(EntryLineColorEffect), "EntryLineColorEffect")]
namespace eShopOnContainers.iOS.Effects
{
public class EntryLineColorEffect : PlatformEffect
{
UITextField control;
protected override void OnAttached()
{
try
{
control = Control as UITextField;
UpdateLineColor();
}
catch (Exception ex)
{
Console.WriteLine("Can't set property on attached control. Error: ", ex.Message);
}
}
protected override void OnDetached()
{
control = null;
}
protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
base.OnElementPropertyChanged(args);
if (args.PropertyName == LineColorBehavior.LineColorProperty.PropertyName ||
args.PropertyName == "Height")
{
Initialize();
UpdateLineColor();
}
}
private void Initialize()
{
var entry = Element as Entry;
if (entry != null)
{
Control.Bounds = new CGRect(0, 0, entry.Width, entry.Height);
}
}
private void UpdateLineColor()
{
BorderLineLayer lineLayer = control.Layer.Sublayers.OfType<BorderLineLayer>()
.FirstOrDefault();
if (lineLayer == null)
{
lineLayer = new BorderLineLayer();
lineLayer.MasksToBounds = true;
lineLayer.BorderWidth = 1.0f;
control.Layer.AddSublayer(lineLayer);
control.BorderStyle = UITextBorderStyle.None;
}
lineLayer.Frame = new CGRect(0f, Control.Frame.Height-1f, Control.Bounds.Width, 1f);
lineLayer.BorderColor = LineColorBehavior.GetLineColor(Element).ToCGColor();
control.TintColor = control.TextColor;
}
private class BorderLineLayer : CALayer
{
}
}
}
Метод OnAttached
получает собственный элемент управления для Xamarin.FormsEntry
элемента управления и обновляет цвет линии путем вызова UpdateLineColor
метода. Переопределение OnElementPropertyChanged
реагирует на изменения привязываемого свойства элемента Entry
управления путем обновления цвета линии, если присоединенное LineColor
свойство изменяется или Height
свойство Entry
изменений. Дополнительные сведения об эффектах см. в статье Эффекты.
Если допустимые данные вводятся в Entry
элемент управления, он применит черную линию к нижней части элемента управления, чтобы указать, что ошибка проверки отсутствует. На рисунке 6-3 показан пример этого.
Рис. 6-3. Черная строка, указывающая на ошибку проверки
Элемент Entry
управления также добавляется DataTrigger
в свою Triggers
коллекцию. В следующем примере кода показано DataTrigger
:
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
...
<Entry.Triggers>
<DataTrigger
TargetType="Entry"
Binding="{Binding UserName.IsValid}"
Value="False">
<Setter Property="behaviors:LineColorBehavior.LineColor"
Value="{StaticResource ErrorColor}" />
</DataTrigger>
</Entry.Triggers>
</Entry>
Это DataTrigger
отслеживает UserName.IsValid
свойство и, если оно становится false
значением, выполняется Setter
, который изменяет LineColor
присоединенное свойство присоединенного LineColorBehavior
поведения на красный. На рисунке 6-4 показан пример этого.
Рис. 6-4. Красная строка, указывающая на ошибку проверки
Строка в элементе Entry
управления останется красной, пока введенные данные недопустимы, в противном случае она изменится на черную, чтобы указать, что введенные данные допустимы.
Дополнительные сведения о триггерах см. в разделе "Триггеры".
Отображение сообщений об ошибках
В пользовательском интерфейсе отображаются сообщения об ошибках проверки в элементах управления Label под каждым элементом управления, данные которого не прошли проверку. В следующем примере кода показано Label
сообщение об ошибке проверки, если пользователь не ввел допустимое имя пользователя:
<Label Text="{Binding UserName.Errors, Converter={StaticResource FirstValidationErrorConverter}}"
Style="{StaticResource ValidationErrorLabelStyle}" />
Каждый Label
из них привязывается к Errors
свойству проверяемого объекта модели представления. Свойство Errors
предоставляется классом ValidatableObject<T>
и имеет тип List<string>
. Errors
Так как свойство может содержать несколько ошибок проверки, FirstValidationErrorConverter
экземпляр используется для получения первой ошибки из коллекции для отображения.
Итоги
Мобильное приложение eShopOnContainers выполняет синхронную проверку свойств модели просмотра и уведомляет пользователя об ошибках проверки, выделите элемент управления, содержащий недопустимые данные, и отображая сообщения об ошибках, которые сообщают пользователю о том, почему данные недопустимы.
Просмотр свойств модели, требующих проверки, имеет тип ValidatableObject<T>
, и каждый ValidatableObject<T>
экземпляр имеет правила проверки, добавленные в его Validations
свойство. Проверка вызывается из модели представления путем вызова Validate
метода экземпляра ValidatableObject<T>
, который извлекает правила проверки и выполняет их для ValidatableObject<T>
Value
свойства. Все ошибки проверки помещаются в Errors
свойство экземпляра ValidatableObject<T>
, а IsValid
свойство экземпляра ValidatableObject<T>
обновляется, чтобы указать, выполнена ли проверка успешно или не выполнена.