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.

Validation classes in the eShopOnContainers mobile app

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ą booleanCheck 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 LoginViewobiekcie , podczas korzystania z pozornych usług. Delegat polecenia wywołuje metodę MockSignInAsync w LoginViewModelobiekcie , 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 LoginViewobiekcie , 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 LoginViewUserName 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 Entryobiekcie , 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 czarny, a komunikat o błędzie zostanie usunięty. Rysunek 6–2 przedstawia element LoginView w aplikacji mobilnej eShopOnContainers, gdy występują błędy walidacji.

Displaying validation errors during login

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 EntryLineColorEffectRoutingEffect 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 czarna linia w dolnej części kontrolki, aby wskazać, że nie ma błędu walidacji. Rysunek 6–3 przedstawia przykład tego.

Black line indicating no validation error

Rysunek 6–3. Czarna 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ę falsewartoś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.

Red line indicating validation error

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 czarny, 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.