Walidacja w aplikacjach dla przedsiębiorstw
Uwaga
Ta książka elektroniczna została opublikowana wiosną 2017 r. i od tego czasu nie została zaktualizowana. Jest wiele w książce, która pozostaje cenna, ale niektóre z materiałów są przestarzałe.
Każda aplikacja, która akceptuje dane wejściowe od użytkowników, powinna upewnić się, że dane wejściowe są prawidłowe. Aplikacja może na przykład sprawdzić, czy dane wejściowe zawierają tylko znaki w określonym zakresie, ma określoną długość lub pasują do określonego formatu. Bez walidacji użytkownik może podać dane, które powodują niepowodzenie aplikacji. Walidacja wymusza reguły biznesowe i uniemożliwia atakującemu wstrzyknięcie złośliwych danych.
W kontekście wzorca Model-View-ViewModel (MVVM) model widoku lub model często będzie wymagany do przeprowadzenia walidacji danych i zasygnalizować wszelkie błędy weryfikacji w widoku, aby użytkownik mógł je poprawić. Aplikacja mobilna eShopOnContainers przeprowadza synchroniczną walidację po stronie klienta właściwości modelu wyświetlania i powiadamia użytkownika o wszelkich błędach walidacji, wyróżniając kontrolkę zawierającą nieprawidłowe dane oraz wyświetlając komunikaty o błędach informujące użytkownika o tym, dlaczego dane są nieprawidłowe. Rysunek 6–1 przedstawia klasy związane z wykonywaniem walidacji w aplikacji mobilnej eShopOnContainers.
Rysunek 6–1. Klasy weryfikacji w aplikacji mobilnej eShopOnContainers
Wyświetl właściwości modelu, które wymagają weryfikacji, są typu ValidatableObject<T>
, a każde ValidatableObject<T>
wystąpienie ma reguły sprawdzania poprawności dodane do jego Validations
właściwości. Walidacja jest wywoływana z modelu widoku przez wywołanie Validate
metody ValidatableObject<T>
wystąpienia, która pobiera reguły walidacji i wykonuje je względem ValidatableObject<T>
Value
właściwości . Wszelkie błędy walidacji są umieszczane we Errors
właściwości ValidatableObject<T>
wystąpienia, a IsValid
właściwość ValidatableObject<T>
wystąpienia jest aktualizowana w celu wskazania, czy walidacja zakończyła się powodzeniem, czy niepowodzeniem.
Powiadomienie o zmianie właściwości jest dostarczane przez ExtendedBindableObject
klasę, dlatego kontrolka może powiązać z IsValid
właściwością ValidatableObject<T>
Entry
wystąpienia w klasie modelu widoku, aby otrzymywać powiadomienia o tym, czy wprowadzone dane są prawidłowe.
Określanie reguł walidacji
Reguły walidacji są określane przez utworzenie klasy pochodzącej z interfejsu IValidationRule<T>
, która jest pokazana w poniższym przykładzie kodu:
public interface IValidationRule<T>
{
string ValidationMessage { get; set; }
bool Check(T value);
}
Ten interfejs określa, że klasa reguły walidacji musi dostarczyć metodę używaną boolean
Check
do przeprowadzania wymaganej weryfikacji, a ValidationMessage
właściwość, której wartość jest komunikatem o błędzie weryfikacji, który będzie wyświetlany w przypadku niepowodzenia walidacji.
Poniższy przykład kodu przedstawia regułę IsNotNullOrEmptyRule<T>
weryfikacji, która służy do sprawdzania poprawności nazwy użytkownika i hasła wprowadzonego przez użytkownika LoginView
podczas korzystania z usług pozorowania w aplikacji mobilnej 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);
}
}
Metoda Check
zwraca wartość wskazującą boolean
, czy argument wartości to null
, pusty, czy składa się tylko z białych znaków.
Mimo że nie jest używana przez aplikację mobilną eShopOnContainers, poniższy przykład kodu przedstawia regułę sprawdzania poprawności na potrzeby sprawdzania poprawności adresów e-mail:
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;
}
}
Metoda Check
zwraca wartość wskazującą boolean
, czy argument wartości jest prawidłowym adresem e-mail. Jest to osiągane przez wyszukanie argumentu wartości dla pierwszego wystąpienia wzorca wyrażenia regularnego określonego w konstruktorze Regex
. Czy wzorzec wyrażenia regularnego został znaleziony w ciągu wejściowym, można określić, sprawdzając wartość Match
właściwości obiektu Success
.
Uwaga
Walidacja właściwości może czasami obejmować właściwości zależne. Przykładem właściwości zależnych jest, gdy zestaw prawidłowych wartości właściwości A zależy od określonej wartości ustawionej we właściwości B. Aby sprawdzić, czy wartość właściwości A jest jedną z dozwolonych wartości, wymaga pobierania wartości właściwości B. Ponadto, gdy wartość właściwości B ulegnie zmianie, właściwość A musi zostać ponownie odnowiona.
Dodawanie reguł walidacji do właściwości
W aplikacji mobilnej eShopOnContainers wyświetl właściwości modelu, które wymagają weryfikacji, są deklarowane jako typu ValidatableObject<T>
, gdzie T
jest typem danych do zweryfikowania. Poniższy przykład kodu przedstawia przykład dwóch takich właściwości:
public ValidatableObject<string> UserName
{
get
{
return _userName;
}
set
{
_userName = value;
RaisePropertyChanged(() => UserName);
}
}
public ValidatableObject<string> Password
{
get
{
return _password;
}
set
{
_password = value;
RaisePropertyChanged(() => Password);
}
}
Aby można było przeprowadzić walidację, reguły weryfikacji należy dodać do Validations
kolekcji każdego ValidatableObject<T>
wystąpienia, jak pokazano w poniższym przykładzie kodu:
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."
});
}
Ta metoda dodaje regułę IsNotNullOrEmptyRule<T>
sprawdzania poprawności do Validations
kolekcji każdego ValidatableObject<T>
wystąpienia, określając wartości właściwości reguły ValidationMessage
walidacji, która określa komunikat o błędzie weryfikacji, który będzie wyświetlany w przypadku niepowodzenia walidacji.
Wyzwalanie walidacji
Metoda sprawdzania poprawności używana w aplikacji mobilnej eShopOnContainers może ręcznie wyzwolić walidację właściwości i automatycznie wyzwolić walidację po zmianie właściwości.
Ręczne wyzwalanie walidacji
Walidacja może być wyzwalana ręcznie dla właściwości modelu widoku. Na przykład ma to miejsce w aplikacji mobilnej eShopOnContainers, gdy użytkownik naciągnie przycisk Zaloguj na LoginView
obiekcie , podczas korzystania z pozornych usług. Delegat polecenia wywołuje metodę MockSignInAsync
w LoginViewModel
obiekcie , która wywołuje walidację, wykonując Validate
metodę, która jest pokazana w poniższym przykładzie kodu:
private bool Validate()
{
bool isValidUser = ValidateUserName();
bool isValidPassword = ValidatePassword();
return isValidUser && isValidPassword;
}
private bool ValidateUserName()
{
return _userName.Validate();
}
private bool ValidatePassword()
{
return _password.Validate();
}
Metoda Validate
przeprowadza walidację nazwy użytkownika i hasła wprowadzonego przez użytkownika w LoginView
obiekcie , wywołując metodę Validate w każdym ValidatableObject<T>
wystąpieniu. Poniższy przykład kodu przedstawia metodę Validate z ValidatableObject<T>
klasy :
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;
}
Ta metoda czyści Errors
kolekcję, a następnie pobiera wszystkie reguły walidacji dodane do kolekcji obiektu Validations
. Metoda Check
dla każdej pobranej reguły walidacji jest wykonywana, a ValidationMessage
wartość właściwości dla każdej reguły sprawdzania poprawności, która nie może zweryfikować, czy dane są dodawane do Errors
kolekcji ValidatableObject<T>
wystąpienia. IsValid
Na koniec właściwość jest ustawiona, a jej wartość jest zwracana do metody wywołującej, wskazując, czy walidacja zakończyła się pomyślnie, czy nie powiodła się.
Wyzwalanie walidacji po zmianie właściwości
Walidacja może być również wyzwalana za każdym razem, gdy zmienia się powiązana właściwość. Na przykład po wyzwoleniu powiązania dwukierunkowego w ustawianiu LoginView
UserName
właściwości lub Password
jest wyzwalana walidacja. W poniższym przykładzie kodu pokazano, jak to się dzieje:
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Behaviors>
<behaviors:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidateUserNameCommand}" />
</Entry.Behaviors>
...
</Entry>
Kontrolka Entry
wiąże się z UserName.Value
właściwością ValidatableObject<T>
wystąpienia, a kolekcja kontrolki Behaviors
ma EventToCommandBehavior
dodane do niego wystąpienie. To zachowanie powoduje wykonanie ValidateUserNameCommand
w odpowiedzi na zdarzenie [TextChanged
] wyzwalane na Entry
obiekcie , który jest zgłaszany, gdy tekst w Entry
zmianach. Z kolei ValidateUserNameCommand
delegat wykonuje metodę ValidateUserName
, która wykonuje Validate
metodę w wystąpieniu ValidatableObject<T>
. W związku z tym za każdym razem, gdy użytkownik wprowadza znak w kontrolce Entry
dla nazwy użytkownika, wykonywana jest walidacja wprowadzonych danych.
Aby uzyskać więcej informacji na temat zachowań, zobacz Implementowanie zachowań.
Wyświetlanie błędów walidacji
Aplikacja mobilna eShopOnContainers powiadamia użytkownika o wszelkich błędach walidacji, wyróżniając kontrolkę zawierającą nieprawidłowe dane z czerwoną linią oraz wyświetlając komunikat o błędzie informujący użytkownika, dlaczego dane są nieprawidłowe poniżej kontrolki zawierającej nieprawidłowe dane. Po skorygowaniu nieprawidłowych danych wiersz zmieni się na, a komunikat o błędzie zostanie usunięty. Rysunek 6–2 przedstawia element LoginView w aplikacji mobilnej eShopOnContainers, gdy występują błędy walidacji.
Rysunek 6–2. Wyświetlanie błędów walidacji podczas logowania
Wyróżnianie kontrolki zawierającej nieprawidłowe dane
Dołączone LineColorBehavior
zachowanie służy do wyróżniania Entry
kontrolek, w których wystąpiły błędy walidacji. Poniższy przykład kodu pokazuje, jak LineColorBehavior
dołączone zachowanie jest dołączone do kontrolki 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>
Kontrolka Entry
używa jawnego stylu, który jest wyświetlany w poniższym przykładzie kodu:
<Style x:Key="EntryStyle"
TargetType="{x:Type Entry}">
...
<Setter Property="behaviors:LineColorBehavior.ApplyLineColor"
Value="True" />
<Setter Property="behaviors:LineColorBehavior.LineColor"
Value="{StaticResource BlackColor}" />
...
</Style>
Ten styl ustawia ApplyLineColor
i LineColor
dołączone właściwości dołączonego LineColorBehavior
zachowania w kontrolce Entry
. Aby uzyskać więcej informacji na temat stylów, zobacz Style.
Gdy wartość dołączonej ApplyLineColor
właściwości jest ustawiona lub zmienia się, LineColorBehavior
dołączone zachowanie wykonuje metodę OnApplyLineColorChanged
, która jest wyświetlana w poniższym przykładzie kodu:
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);
}
}
}
}
Parametry tej metody zapewniają wystąpienie kontrolki, do którego jest dołączone zachowanie, oraz stare i nowe wartości dołączonej ApplyLineColor
właściwości. Klasa EntryLineColorEffect
jest dodawana do kolekcji kontrolki Effects
, jeśli ApplyLineColor
dołączona właściwość to true
, w przeciwnym razie zostanie usunięta z kolekcji kontrolki Effects
. Aby uzyskać więcej informacji na temat zachowań, zobacz Implementowanie zachowań.
Podklasy EntryLineColorEffect
RoutingEffect
klasy i są wyświetlane w poniższym przykładzie kodu:
public class EntryLineColorEffect : RoutingEffect
{
public EntryLineColorEffect() : base("eShopOnContainers.EntryLineColorEffect")
{
}
}
Klasa RoutingEffect
reprezentuje efekt niezależny od platformy, który opakowuje wewnętrzny efekt specyficzny dla platformy. Upraszcza to proces usuwania efektu, ponieważ nie ma dostępu w czasie kompilacji do informacji o typie dla efektu specyficznego dla platformy. Wywołuje EntryLineColorEffect
konstruktor klasy bazowej, przekazując parametr składający się z łączenia nazwy grupy rozpoznawania i unikatowy identyfikator określony w każdej klasie efektu specyficznego dla platformy.
Poniższy przykład kodu przedstawia implementację eShopOnContainers.EntryLineColorEffect
dla systemu 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
{
}
}
}
Metoda OnAttached
pobiera natywną kontrolkę dla kontrolki Xamarin.FormsEntry
i aktualizuje kolor linii przez wywołanie UpdateLineColor
metody . Przesłonięcia OnElementPropertyChanged
reagują na zmiany właściwości, które można powiązać w kontrolce Entry
, aktualizując kolor linii, jeśli dołączona LineColor
właściwość ulegnie zmianie lub Height
właściwości Entry
zmian. Aby uzyskać więcej informacji na temat efektów, zobacz Efekty.
Po wprowadzeniu prawidłowych danych w kontrolce Entry
zostanie zastosowana linia w dolnej części kontrolki, aby wskazać, że nie ma błędu walidacji. Rysunek 6–3 przedstawia przykład tego.
Rysunek 6–3. linia wskazująca błąd walidacji
Kontrolka Entry
ma DataTrigger
również dodany element do swojej Triggers
kolekcji. Poniższy przykład kodu przedstawia element 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>
Spowoduje to DataTrigger
monitorowanie UserName.IsValid
właściwości, a jeśli jej wartość stanie się false
wartością Setter
, wykonuje właściwość , która zmienia dołączoną LineColor
właściwość dołączonego LineColorBehavior
zachowania na czerwono. Rysunek 6–4 przedstawia przykład tego.
Rysunek 6–4. Czerwona linia wskazująca błąd walidacji
Wiersz w kontrolce Entry
pozostanie czerwony, gdy wprowadzone dane są nieprawidłowe. W przeciwnym razie zmieni się na, aby wskazać, że wprowadzone dane są prawidłowe.
Aby uzyskać więcej informacji na temat wyzwalaczy, zobacz Wyzwalacze.
Wyświetlanie komunikatów o błędach
Interfejs użytkownika wyświetla komunikaty o błędach walidacji w kontrolkach Etykieta poniżej każdej kontrolki, której weryfikacja danych nie powiodła się. Poniższy przykład kodu przedstawia Label
komunikat o błędzie weryfikacji, jeśli użytkownik nie wprowadził prawidłowej nazwy użytkownika:
<Label Text="{Binding UserName.Errors, Converter={StaticResource FirstValidationErrorConverter}}"
Style="{StaticResource ValidationErrorLabelStyle}" />
Każda Label
z nich wiąże się z właściwością Errors
obiektu modelu widoku, który jest weryfikowany. Właściwość Errors
jest dostarczana przez klasę ValidatableObject<T>
i jest typu List<string>
. Errors
Ponieważ właściwość może zawierać wiele błędów walidacji, FirstValidationErrorConverter
wystąpienie jest używane do pobierania pierwszego błędu z kolekcji na potrzeby wyświetlania.
Podsumowanie
Aplikacja mobilna eShopOnContainers przeprowadza synchroniczną walidację po stronie klienta właściwości modelu wyświetlania i powiadamia użytkownika o wszelkich błędach walidacji, wyróżniając kontrolkę zawierającą nieprawidłowe dane oraz wyświetlając komunikaty o błędach informujące użytkownika, dlaczego dane są nieprawidłowe.
Wyświetl właściwości modelu, które wymagają weryfikacji, są typu ValidatableObject<T>
, a każde ValidatableObject<T>
wystąpienie ma reguły sprawdzania poprawności dodane do jego Validations
właściwości. Walidacja jest wywoływana z modelu widoku przez wywołanie Validate
metody ValidatableObject<T>
wystąpienia, która pobiera reguły walidacji i wykonuje je względem ValidatableObject<T>
Value
właściwości . Wszelkie błędy walidacji są umieszczane we Errors
właściwości ValidatableObject<T>
wystąpienia, a IsValid
właściwość ValidatableObject<T>
wystąpienia jest aktualizowana w celu wskazania, czy walidacja zakończyła się powodzeniem, czy niepowodzeniem.