使用Xamarin.Forms的企业应用程序模式(电子书)--验证

简介: 任何接受用户输入的应用程序都应确保输入有效。 例如,应用程序可以检查仅包含特定范围内的字符的输入,具有一定长度,或匹配特定格式。 没有验证,用户可以提供导致应用失败的数据。 验证强制执行业务规则,并防止攻击者注入恶意数据。

任何接受用户输入的应用程序都应确保输入有效。 例如,应用程序可以检查仅包含特定范围内的字符的输入,具有一定长度,或匹配特定格式。 没有验证,用户可以提供导致应用失败的数据。 验证强制执行业务规则,并防止攻击者注入恶意数据。

Model-ViewModel-Model(MVVM)模式的上下文中,通常需要视图模型或模型来执行数据验证,并向视图发出任何验证错误,以便用户可以对其进行更正。 eShopOnContainers移动应用程序执行视图模型属性的同步客户端验证,并通过突出显示包含无效数据的控件,并通过显示通知用户为什么数据无效的错误消息来通知用户任何验证错误。 图6-1显示了在eShopOnContainers移动应用程序中执行验证所涉及的类。

 

6-1eShopOnContainers移动应用程序中的验证类

查看需要验证的模型属性的类型为ValidatableObject ,每个ValidatableObject 实例将验证规则添加到其Validations属性中。 通过调用ValidatableObject 实例的Validate方法从视图模型中调用验证,该实例检索验证规则并根据ValidatableObject Value属性执行验证规则。 任何验证错误都被放置在ValidatableObject 实例的Errors属性中,并且ValidatableObject 实例的IsValid属性被更新,以指示验证是成功还是失败。

属性更改通知由ExtendedBindableObject类提供,因此Entry控件可以绑定到视图模型类中的ValidatableObject 实例的IsValid属性,以通知输入的数据是否有效。

指定验证规则

验证规则通过创建从IValidationRule 接口派生的类来指定,如下面的代码示例所示:


点击(此处)折叠或打开

  1. public interface IValidationRuleT>
  2. {
  3.     string ValidationMessage { get; set; }
  4.     bool Check(T value);
  5. }


此接口指定验证规则类必须提供用于执行所需验证的布尔检查方法,以及ValidationMessage属性,其值为验证失败时将显示的验证错误消息。

以下代码示例显示了IsNotNullOrEmptyRule 验证规则,用于在eShopOnContainers移动应用程序中使用模拟服务时,验证用户在LoginView上输入的用户名和密码:


点击(此处)折叠或打开

  1. public class IsNotNullOrEmptyRuleT> : IValidationRuleT>
  2. {
  3.     public string ValidationMessage { get; set; }

  4.     public bool Check(T value)
  5.     {
  6.         if (value == null)
  7.         {
  8.             return false;
  9.         }

  10.         var str = value as string;
  11.         return !string.IsNullOrWhiteSpace(str);
  12.     }
  13. }


Check方法返回一个布尔值,指示value参数是空值,空值还是仅由空格字符组成。

虽然eShopOnContainers手机应用程序未使用,但以下代码示例显示验证电子邮件地址的验证规则:


点击(此处)折叠或打开

  1. public class EmailRuleT> : IValidationRuleT>
  2. {
  3.     public string ValidationMessage { get; set; }

  4.     public bool Check(T value)
  5.     {
  6.         if (value == null)
  7.         {
  8.             return false;
  9.         }

  10.         var str = value as string;
  11.         Regex regex = new Regex(@"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$");
  12.         Match match = regex.Match(str);

  13.         return match.Success;
  14.     }
  15. }


Check方法返回一个布尔值,表示value参数是否是有效的电子邮件地址。 这是通过搜索value参数来获得第一次出现在正则表达式模式中指定的正则表达式构造函数。 是否在输入字符串中找到正则表达式模式可以通过检查Match对象的Success属性的值来确定。

注意:属性验证有时可能涉及依赖属性。 依赖属性的示例是属性A的有效值集合取决于在属性B中设置的特定值。要检查属性A的值是允许的值之一将涉及检索属性B的值 此外,当属性B的值更改时,属性A将需要重新生效。

向属性添加验证规则

eShopOnContainers手机应用程序中,需要验证的视图模型属性被声明为ValidatableObject 类型,其中T是要验证的数据的类型。 以下代码示例显示了两个此类属性的示例:


点击(此处)折叠或打开

  1. public ValidatableObjectstring> UserName
  2. {
  3.     get
  4.     {
  5.         return _userName;
  6.     }
  7.     set
  8.     {
  9.         _userName = value;
  10.         RaisePropertyChanged(() => UserName);
  11.     }
  12. }

  13. public ValidatableObjectstring> Password
  14. {
  15.     get
  16.     {
  17.         return _password;
  18.     }
  19.     set
  20.     {
  21.         _password = value;
  22.         RaisePropertyChanged(() => Password);
  23.     }
  24. }


为了进行验证,验证规则必须添加到每个ValidatableObject 实例的Validations集合中,如以下代码示例所示:


点击(此处)折叠或打开

  1. private void AddValidations()
  2. {
  3.     _userName.Validations.Add(new IsNotNullOrEmptyRulestring>
  4.     {
  5.         ValidationMessage = "A username is required."
  6.     });
  7.     _password.Validations.Add(new IsNotNullOrEmptyRulestring>
  8.     {
  9.         ValidationMessage = "A password is required."
  10.     });
  11. }


此方法将IsNotNullOrEmptyRule 验证规则添加到每个ValidatableObject 实例的Validations集合中,指定验证规则的ValidationMessage属性的值,该属性指定验证失败时将显示的验证错误消息。

触发验证

eShopOnContainers移动应用程序中使用的验证方法可以手动触发属性的验证,并在属性更改时自动触发验证。

手动触发验证

视图模型属性可以手动触发验证。 例如,当使用模拟服务时,当用户点击LoginView上的Login按钮时,会发生在eShopOnContainers移动应用程序中。 命令委托在LoginViewModel中调用MockSignInAsync方法,该方法通过执行Validate方法调用验证,该方法显示在以下代码示例中:


点击(此处)折叠或打开

  1. private bool Validate()
  2. {
  3.     bool isValidUser = ValidateUserName();
  4.     bool isValidPassword = ValidatePassword();
  5.     return isValidUser && isValidPassword;
  6. }

  7. private bool ValidateUserName()
  8. {
  9.     return _userName.Validate();
  10. }

  11. private bool ValidatePassword()
  12. {
  13.     return _password.Validate();
  14. }


Validate方法通过调用每个ValidatableObject 实例上的Validate方法来验证用户在LoginView上输入的用户名和密码。 以下代码示例显示了ValidatableObject 类中的Validate方法:


点击(此处)折叠或打开

  1. public bool Validate()
  2. {
  3.     Errors.Clear();

  4.     IEnumerablestring> errors = _validations
  5.         .Where(v => !v.Check(Value))
  6.         .Select(v => v.ValidationMessage);

  7.     Errors = errors.ToList();
  8.     IsValid = !Errors.Any();

  9.     return this.IsValid;
  10. }


此方法将清除Errors集合,然后检索添加到对象的Validations集合的任何验证规则。 执行每个检索的验证规则的检查方法,并且无法验证数据的任何验证规则的ValidationMessage属性值都将添加到ValidatableObject 实例的错误集合中。 最后,设置IsValid属性,并将其值返回给调用方法,指示验证是成功还是失败。

属性更改时触发验证

绑定属性更改时也会自动触发验证。 例如,当LoginView中的双向绑定设置UserName或Password属性时,会触发验证。 以下代码示例演示了如何发生这种情况:

 

点击(此处)折叠或打开

  1. Entry Text="{Binding UserName.Value, Mode=TwoWay}">
  2.     Entry.Behaviors>
  3.         behaviors:EventToCommandBehavior
  4.             EventName="TextChanged"
  5.             Command="{Binding ValidateUserNameCommand}" />
  6.     /Entry.Behaviors>
  7.     ...
  8. /Entry>


Entry控件绑定到ValidatableObject 实例的UserName.Value属性,控件的Behaviors集合中添加了一个EventToCommandBehavior实例。 响应于条目中的[TextChanged]事件触发,此行为执行ValidateUserNameCommand,当条目中的文本更改时引发。 反过来,ValidateUserNameCommand委托执行ValidateUserName方法,该方法在ValidatableObject 实例上执行Validate方法。 因此,每当用户在用户名的Entry控件中输入一个字符时,进行输入数据的验证。

有关行为的更多信息,请参阅实现行为

显示验证错误

ShopOnContainers手机应用程序通过突出显示包含无效数据的控件以红色线条通知用户任何验证错误,并显示一条错误消息,通知用户为什么数据在包含无效数据的控件下无效。 当无效数据更正时,行变为黑色,并删除错误消息。 eShopOnContainers手机应用程序中的LoginView如图6-2所示。

 

6-2:登录时显示验证错误

突出显示包含无效数据的控件

LineColorBehavior附加行为用于突出显示已发生验证错误的Entry控件。 以下代码示例显示如何将LineColorBehavior附加行为附加到Entry控件:

 

点击(此处)折叠或打开

  1. Entry Text="{Binding UserName.Value, Mode=TwoWay}">
  2.     Entry.Style>
  3.         OnPlatform x:TypeArguments="Style"
  4.           iOS="{StaticResource EntryStyle}"
  5.           Android="{StaticResource EntryStyle}"
  6.           WinPhone="{StaticResource UwpEntryStyle}"/>
  7.     /Entry.Style>
  8.     ...
  9. /Entry>


Entry控件使用显式样式,如下面的代码示例所示:

 

点击(此处)折叠或打开

  1. Style x:Key="EntryStyle"
  2.        TargetType="{x:Type Entry}">
  3.     ...
  4.     Setter Property="behaviors:LineColorBehavior.ApplyLineColor"
  5.             Value="True" />
  6.     Setter Property="behaviors:LineColorBehavior.LineColor"
  7.             Value="{StaticResource BlackColor}" />
  8.     ...
  9. /Style>


此样式将EntryColorBehavior附加行为的ApplyLineColor和LineColor附加属性设置为Entry控件。 有关样式的更多信息,请参阅Xamarin开发人员中心的样式。

ApplyLineColor附加属性的值被设置或更改时,LineColorBehavior附加行为执行OnApplyLineColorChanged方法,如下面的代码示例所示:


点击(此处)折叠或打开

  1. public static class LineColorBehavior
  2. {
  3.     ...
  4.     private static void OnApplyLineColorChanged(
  5.                 BindableObject bindable, object oldValue, object newValue)
  6.     {
  7.         var view = bindable as View;
  8.         if (view == null)
  9.         {
  10.             return;
  11.         }

  12.         bool hasLine = (bool)newValue;
  13.         if (hasLine)
  14.         {
  15.             view.Effects.Add(new EntryLineColorEffect());
  16.         }
  17.         else
  18.         {
  19.             var entryLineColorEffectToRemove =
  20.                     view.Effects.FirstOrDefault(e => e is EntryLineColorEffect);
  21.             if (entryLineColorEffectToRemove != null)
  22.             {
  23.                 view.Effects.Remove(entryLineColorEffectToRemove);
  24.             }
  25.         }
  26.     }
  27. }


此方法的参数提供了行为附加到的控件的实例以及ApplyLineColor附加属性的旧值和新值。 如果ApplyLineColor附加属性为true,则EntryLineColorEffect类将添加到控件的“效果”集合中,否则将从控件的“效果”集合中移除。 有关行为的更多信息,请参阅实现行为

EntryLineColorEffectRoutingEffect类分类,并显示在以下代码示例中:


点击(此处)折叠或打开

  1. public class EntryLineColorEffect : RoutingEffect
  2. {
  3.     public EntryLineColorEffect() : base("eShopOnContainers.EntryLineColorEffect")
  4.     {
  5.     }
  6. }


RoutingEffect类代表一个平台无关的效果,它包含了特定于平台的内部效果。 这简化了效果删除过程,因为没有编译时访问类型信息以获取平台特定的效果。 EntryLineColorEffect调用基类构造函数,传入一个包含分辨率组名称的连接和每个特定于平台的效果类指定的唯一ID的参数。

以下代码示例显示iOS的eShopOnContainers.EntryLineColorEffect实现:


点击(此处)折叠或打开

  1. [assembly: ResolutionGroupName("eShopOnContainers")]
  2. [assembly: ExportEffect(typeof(EntryLineColorEffect), "EntryLineColorEffect")]
  3. namespace eShopOnContainers.iOS.Effects
  4. {
  5.     public class EntryLineColorEffect : PlatformEffect
  6.     {
  7.         UITextField control;

  8.         protected override void OnAttached()
  9.         {
  10.             try
  11.             {
  12.                 control = Control as UITextField;
  13.                 UpdateLineColor();
  14.             }
  15.             catch (Exception ex)
  16.             {
  17.                 Console.WriteLine("Can't set property on attached control. Error: ", ex.Message);
  18.             }
  19.         }

  20.         protected override void OnDetached()
  21.         {
  22.             control = null;
  23.         }

  24.         protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
  25.         {
  26.             base.OnElementPropertyChanged(args);

  27.             if (args.PropertyName == LineColorBehavior.LineColorProperty.PropertyName ||
  28.                 args.PropertyName == "Height")
  29.             {
  30.                 Initialize();
  31.                 UpdateLineColor();
  32.             }
  33.         }

  34.         private void Initialize()
  35.         {
  36.             var entry = Element as Entry;
  37.             if (entry != null)
  38.             {
  39.                 Control.Bounds = new CGRect(0, 0, entry.Width, entry.Height);
  40.             }
  41.         }

  42.         private void UpdateLineColor()
  43.         {
  44.             BorderLineLayer lineLayer = control.Layer.Sublayers.OfTypeBorderLineLayer>()
  45.                                                              .FirstOrDefault();

  46.             if (lineLayer == null)
  47.             {
  48.                 lineLayer = new BorderLineLayer();
  49.                 lineLayer.MasksToBounds = true;
  50.                 lineLayer.BorderWidth = 1.0f;
  51.                 control.Layer.AddSublayer(lineLayer);
  52.                 control.BorderStyle = UITextBorderStyle.None;
  53.             }

  54.             lineLayer.Frame = new CGRect(0f, Control.Frame.Height-1f, Control.Bounds.Width, 1f);
  55.             lineLayer.BorderColor = LineColorBehavior.GetLineColor(Element).ToCGColor();
  56.             control.TintColor = control.TextColor;
  57.         }

  58.         private class BorderLineLayer : CALayer
  59.         {
  60.         }
  61.     }
  62. }


OnAttached方法检索Xamarin.Forms Entry控件的本机控件,并通过调用UpdateLineColor方法来更新行颜色。 OnElementPropertyChanged覆盖通过更改线颜色(如果附加的LineColor属性更改)或Entry的Height属性更改来响应Entry控件上的可绑定属性更改。 有关效果的更多信息,请参阅对Xamarin开发人员中心的影响。

当在Entry控件中输入有效数据时,它将在控件的底部应用一条黑线,以表示没有验证错误。 图6-3显示了一个例子。

 

6-3:黑线表示无验证错误

Entry控件还将DataTrigger添加到其Triggers集合中。 以下代码示例显示DataTrigger:

 

点击(此处)折叠或打开

  1. Entry Text="{Binding UserName.Value, Mode=TwoWay}">
  2.     ...
  3.     Entry.Triggers>
  4.         DataTrigger
  5.             TargetType="Entry"
  6.             Binding="{Binding UserName.IsValid}"
  7.             Value="False">
  8.             Setter Property="behaviors:LineColorBehavior.LineColor"
  9.                     Value="{StaticResource ErrorColor}" />
  10.         /DataTrigger>
  11.     /Entry.Triggers>
  12. /Entry>


DataTrigger监视UserName.IsValid属性,如果值为false,它将执行Setter,它将LineColorBehavior附加行为的LineColor附加属性更改为红色。 图6-4显示了一个例子。

 

6-4:红线表示验证错误

输入控件中的行将保持红色,而输入的数据无效,否则将变为黑色,表示输入的数据有效。

有关触发器的更多信息,请参阅Xamarin开发人员中心的触发器

显示错误信息

UI会在数据失败验证的每个控件下面的Label控件中显示验证错误消息。 以下代码示例显示如果用户未输入有效的用户名,则该标签显示验证错误消息:


点击(此处)折叠或打开

  1. Label Text="{Binding UserName.Errors, Converter={StaticResource FirstValidationErrorConverter}"
  2.        Style="{StaticResource ValidationErrorLabelStyle}" />


每个标签绑定到正在验证的视图模型对象的Errors属性。 错误属性由ValidatableObject 类提供,类型为List 。 因为Errors属性可能包含多个验证错误,FirstValidationErrorConverter实例用于从集合中检索第一个错误以进行显示。

概要

eShopOnContainers移动应用程序执行视图模型属性的同步客户端验证,并通过突出显示包含无效数据的控件,并通过显示通知用户为什么数据无效的错误消息通知用户任何验证错误。

查看需要验证的模型属性的类型为ValidatableObject ,每个ValidatableObject 实例将验证规则添加到其Validations属性中。 通过调用ValidatableObject 实例的Validate方法从视图模型中调用验证,该实例检索验证规则并根据ValidatableObject Value属性执行验证规则。 任何验证错误都被放置在ValidatableObject 实例的Errors属性中,并且ValidatableObject 实例的IsValid属性被更新,以指示验证是成功还是失败。

目录
相关文章
|
前端开发 .NET 测试技术
使用Xamarin.Forms开发企业应用程序
与企业应用开发人员多年合作,我们知道他们面临着几个挑战,其中包括: 随着时间的推移,需求不断变化 新的商机和挑战 在开发期间持续的反馈可能会显着影响应用程序的范围和要求 考虑到这一点,重要的是构建既灵活又可随时间轻松修改或扩展的应用程序。
1086 0
|
测试技术 Android开发 iOS开发
使用Xamarin.Forms的企业应用程序模式(电子书)--介绍
无论平台如何,企业应用开发人员都面临着几个挑战: 随时间变化的应用程序要求。 新的商机和挑战。 开发期间持续的反馈可能会显着影响应用程序的范围和要求。
1429 0
|
前端开发 测试技术
使用Xamarin.Forms的企业应用程序模式(电子书)--单元测试
移动应用程序具有独特的问题,桌面和基于Web的应用程序不必担心。移动用户将因其使用的设备,网络连接,服务可用性以及一系列其他因素而有所不同。因此,应该测试移动应用程序,因为它们将被用于现实世界,以提高其质量,可靠性和性能。
940 0
|
Web App开发 存储 缓存
使用Xamarin.Forms的企业应用程序模式(电子书)--访问远程数据
许多现代的基于Web的解决方案利用由Web服务器托管的Web服务来为远程客户端应用程序提供功能。 Web服务公开的操作构成Web API。 客户端应用程序应该能够在不知道API暴露的数据或操作如何实现的情况下使用Web API。
1262 0
|
Web App开发 存储 .NET
使用Xamarin.Forms的企业应用程序模式(电子书)--认证和授权
身份验证是从用户获取身份验证凭证(例如姓名和密码)以及根据权限验证这些凭据的过程。如果凭据有效,则提交凭据的实体被认为是认证身份。一旦身份被认证,授权过程将确定该身份是否可以访问给定的资源。
1407 0
|
存储 消息中间件 Docker
使用Xamarin.Forms的企业应用程序模式(电子书)--容器化微服务
开发客户端 - 服务器应用程序的重点是建立在每个层中使用特定技术的分层应用程序。这样的应用通常被称为单片应用,并且被封装在用于峰值负载预分配的硬件上。这种开发方法的主要缺点是每个层级组件之间的紧密耦合,各个组件不能轻易缩放,并且测试成本。
993 0
|
存储 API 容器
使用Xamarin.Forms的企业应用程序模式(电子书)--配置管理
设置允许将配置应用程序行为的数据与代码分离,允许在不重新构建应用程序的情况下更改行为。有两种类型的设置:应用设置和用户设置。 应用设置是应用程序创建和管理的数据。它可以包括固定Web服务端点,API密钥和运行时状态等数据。
1059 0
使用Xamarin.Forms的企业应用程序模式(电子书)--松散耦合部件之间的通信
发布订阅模式是一种消息传递模式,其中发布者发送消息而不知道任何接收者(称为订阅者)。 类似地,订阅者听取特定的消息,而不了解任何发布者。 .NET中的事件实现了发布 - 订阅模式,并且是不需要松散耦合的组件之间的通信层的最简单和直接的方法,例如控件和包含它的页面。
1080 0
在Xamarin.Forms企业应用程序中进行验证
我们在五月份发布了一本《使用Xamarin.Forms的企业应用程序模式》的电子书。电子书专注于开发Xamarin.Forms更容易测试,维护和发展的企业应用程序的核心模式和架构指导。
1088 0
|
6月前
|
开发工具 Android开发 iOS开发
使用xamarin开发Android、iOS报错failed to open directory: 系统找不到指定的文件
使用vs2019学习xamarin时,创建新程序。使用模拟器真机等测试都报错如下图错误: ![请在此添加图片描述](https://developer-private-1258344699.cos.ap-guangzhou.myqcloud.com/column/article/5877188/20231030-de8ce5fd.png?x-cos-security-token=r4KyZDEowPT0kGTL0LqE8EnwfN1Nzexadb05dcffed3939ff8d7591c528c01706nvpGSE93QwHpZM8NwhJNTZctNRQa0l3KDhEnqj8P7d8t
63 0
使用xamarin开发Android、iOS报错failed to open directory: 系统找不到指定的文件