Enterprise Apps での検証

Note

この電子ブックは 2017 年の春に発行され、それ以来更新されていません。 貴重なままの本に多くがありますが、資料の一部は時代遅れです。

ユーザーから入力を受け取るアプリでは、その入力が有効であることを確認する必要があります。 たとえば、アプリでは、入力に特定の範囲の文字のみが含まれているかどうか、入力が一定の長さであるかどうか、または特定の形式に一致しているかどうかを確認します。 検証を行わないと、ユーザーはアプリが失敗する原因となるデータを提供する危険性があります。 検証はビジネス ルールを適用し、攻撃者が悪意のあるデータを挿入するのを防ぎます。

Model-View-ViewModel (MVVM) パターンのコンテキストでは、多くの場合、ユーザーが修正できるように、データ検証を実行し、検証エラーをビューに通知するためにビュー モデルまたはモデルが必要になります。 eShopOnContainers モバイル アプリは、ビュー モデルのプロパティのクライアント側の同期検証を実行し、無効なデータを含むコントロールを強調表示し、データが無効な理由をユーザーに通知するエラー メッセージを表示することで、検証エラーをユーザーに通知します。 図 6-1 は、eShopOnContainers モバイル アプリでの検証の実行に関連するクラスを示しています。

検証クラス eShopOnContainers モバイル アプリの検証クラス

図 6-1: eShopOnContainers モバイル アプリの検証クラス

検証を必要とするビュー モデル プロパティは型 ValidatableObject<T> であり、各 ValidatableObject<T> インスタンスには、その Validations プロパティに検証規則が追加されています。 検証は、検証規則を取得し、 プロパティにValidate対してValidatableObject<T>Value実行する インスタンスの ValidatableObject<T> メソッドを呼び出すことによって、ビュー モデルから呼び出されます。 検証エラーはインスタンスの プロパティにErrors配置されIsValid、検証が成功したか失敗したかをValidatableObject<T>示すためにインスタンスの プロパティが更新ValidatableObject<T>されます。

プロパティ変更通知は ExtendedBindableObject クラスによって提供されるため、Entry コントロールをビュー モデル クラスの ValidatableObject<T> インスタンスの IsValid プロパティにバインドして、入力されたデータが有効であるかどうかを通知できます。

検証規則の指定

検証規則を指定するには、次のコード例に示す IValidationRule<T> インターフェイスから派生するクラスを作成します。

public interface IValidationRule<T>  
{  
    string ValidationMessage { get; set; }  
    bool Check(T value);  
}

このインターフェイスは、検証ルール クラスが、必要な検証を実行するために使用されるメソッドとValidationMessage、検証が失敗した場合に表示される検証エラー メッセージを値とするプロパティを提供booleanCheckする必要があることを指定します。

次のコード例は、eShopOnContainers モバイル アプリでモック サービスを使用するときに、 でLoginViewユーザーが入力したユーザー名とパスワードの検証を実行するために使用される検証規則を示していますIsNotNullOrEmptyRule<T>

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);  
    }  
}

メソッドは Checkboolean value 引数が 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;  
    }  
}

メソッドは Checkboolean value 引数が有効な電子メール アドレスであるかどうかを示す を返します。 これは、Regex コンストラクターで指定された正規表現パターンが最初に出現する値引数を検索することによって実現されます。 入力文字列で正規表現パターンが見つかったかどうかは、オブジェクトSuccessの プロパティの値をMatch確認することで判断できます。

注意

プロパティの検証には、依存プロパティが含まれる場合があります。 依存プロパティの一例としては、プロパティ A の有効な値のセットが、プロパティ B で設定された特定の値に依存する場合があります。プロパティ A の値が許可された値の 1 つであることを確認するには、プロパティ B の値を取得する必要があります。さらに、プロパティ B の値が変更された場合、プロパティ A を再検証する必要があります。

プロパティへの検証規則の追加

eShopOnContainers モバイル アプリでは、検証を必要とするビュー モデルのプロパティは 型 ValidatableObject<T>として宣言されます。ここで T 、 は検証するデータの型です。 次のコード例は、このような 2 つのプロパティの例を示します。

public ValidatableObject<string> UserName  
{  
    get  
    {  
        return _userName;  
    }  
    set  
    {  
        _userName = value;  
        RaisePropertyChanged(() => UserName);  
    }  
}  

public ValidatableObject<string> Password  
{  
    get  
    {  
        return _password;  
    }  
    set  
    {  
        _password = value;  
        RaisePropertyChanged(() => Password);  
    }  
}

検証を行うには、次のコード例に示すように、検証規則を ValidationsValidatableObject<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これが発生します。 コマンド デリゲートは LoginViewModelMockSignInAsync メソッドを呼び出します。これは、次のコード例に示すように、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、 でユーザーが入力したユーザー名とパスワードの検証を実行します。各ValidatableObject<T>インスタンスで LoginViewValidate メソッドを呼び出します。 次のコード例は、 クラスの 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 プロパティ値が ValidatableObject<T> インスタンスの Errors コレクションに追加されます。 最後に、IsValid プロパティが設定され、その値が呼び出し元のメソッドに返され、検証が成功したか失敗したかを示します。

プロパティが変更されたときに検証をトリガーする

バインドされたプロパティが変更されるたびに検証をトリガーすることもできます。 たとえば、LoginView 内の両方向のバインドによって UserName または Password プロパティが設定されると、検証がトリガーされます。 次のコード例は、これがどのように発生するかを示します。

<Entry Text="{Binding UserName.Value, Mode=TwoWay}">  
    <Entry.Behaviors>  
        <behaviors:EventToCommandBehavior  
            EventName="TextChanged"  
            Command="{Binding ValidateUserNameCommand}" />  
    </Entry.Behaviors>  
    ...  
</Entry>

Entry コントロールが ValidatableObject<T> インスタンスの UserName.Value プロパティにバインドされ、このコントロールの Behaviors コレクションには、EventToCommandBehavior インスタンスが追加されています。 この動作は、 ValidateUserNameCommand で発生する [TextChanged] イベントに Entry応答して を実行します。これは、 内の Entry テキストが変更されたときに発生します。 次に ValidateUserNameCommand デリゲートによって ValidateUserName メソッドが実行され、これは ValidatableObject<T> インスタンスで Validate メソッドを実行します。 したがって、ユーザーがユーザー名の Entry コントロールに文字を入力するたびに、入力されたデータの検証が実行されます。

動作の詳細については、「動作の 実装」を参照してください。

検証エラーの表示

eShopOnContainers モバイル アプリは、無効なデータを含むコントロールを赤い線で強調表示し、無効なデータを含むコントロールの下にデータが無効である理由をユーザーに通知するエラー メッセージを表示することで、検証エラーをユーザーに通知します。 無効なデータが修正されると、行が黒に変わり、エラー メッセージが削除されます。 図 6-2 は、検証エラーが発生した場合の eShopOnContainers モバイル アプリの LoginView を示しています。

ログイン中の検証エラーの表示

図 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添付動作の LineColorBehavior プロパティと LineColor 添付プロパティをEntry設定します。 スタイルについて詳しくは、「Styles」(スタイル) をご覧ください。

添付プロパティの 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添付プロパティが の場合ApplyLineColorは、 クラスがtrueコントロールのEffectsコレクションに追加されます。それ以外の場合は、コントロールのEffectsコレクションから削除されます。 動作の詳細については、「動作の 実装」を参照してください。

EntryLineColorEffect クラスを RoutingEffect サブクラス化し、次のコード例に示します。

public class EntryLineColorEffect : RoutingEffect  
{  
    public EntryLineColorEffect() : base("eShopOnContainers.EntryLineColorEffect")  
    {  
    }  
}

クラスは RoutingEffect 、プラットフォーム固有の内部効果をラップするプラットフォームに依存しない効果を表します。 これは、プラットフォーム固有のエフェクトの型情報へのコンパイル時アクセスがないため、エフェクトの削除プロセスを簡略化します。 は EntryLineColorEffect 基底クラス コンストラクターを呼び出し、解決グループ名の連結と、各プラットフォーム固有の効果クラスで指定された一意の ID で構成されるパラメーターを渡します。

次のコード例は、iOS の実装を eShopOnContainers.EntryLineColorEffect 示しています。

[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: 検証エラーがないことを示す黒い線

コントロールには EntryDataTrigger その 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され、アタッチされた動作のLineColorBehavior添付プロパティが赤に変更LineColorされます。 この例を図 6-4 に示します。

検証エラーを示す赤い線

図 6-4: 検証エラーを示す赤い線

入力されたデータが無効な場合、コントロール内 Entry の行は赤のままです。それ以外の場合は、入力されたデータが有効であることを示すために黒に変更されます。

トリガーの詳細については、「 トリガー」を参照してください。

エラー メッセージの表示

UI では、データが検証に失敗した各コントロールの下の 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 プロパティに検証規則が追加されています。 検証は、インスタンスの メソッドValidatableObject<T>Validate呼び出すことによってビュー モデルから呼び出されます。これにより、検証規則が取得され、 プロパティにValidatableObject<T>Value対して実行されます。 検証エラーはインスタンスの ErrorsValidatableObject<T>プロパティに配置され、検証が IsValid 成功したか失敗したかを示すためにインスタンスの ValidatableObject<T> プロパティが更新されます。