Validação
Dica
Esse conteúdo é um trecho do livro eletrônico, Padrões de Aplicativo Empresarial Usando .NETMAUI, disponível em .NET Docs ou em PDF para download gratuito que pode ser lido off-line.
Todo aplicativo que aceitar a entrada dos usuários precisará garantir que a entrada seja válida. Um aplicativo pode, por exemplo, verificar se a entrada contém apenas caracteres em determinado intervalo, tem determinado comprimento ou corresponde a um formato específico. Sem validação, um usuário pode fornecer dados que fazem com que o aplicativo falhe. A validação adequada impõe regras de negócios e pode ajudar a impedir que um invasor insira dados mal-intencionados.
No contexto do padrão MVVM (Model-View-ViewModel), um modelo ou modelo de exibição geralmente será exigido para executar a validação de dados e sinalizar eventuais erros de validação para a exibição a fim de que o usuário possa corrigi-los. O aplicativo multiplataforma eShop executa a validação síncrona do lado do cliente das propriedades do modelo de exibição e notifica o usuário sobre quaisquer erros de validação, realçando o controle que contém os dados inválidos e exibindo mensagens de erro que informam ao usuário por que os dados são inválidos. A imagem abaixo mostra as classes envolvidas na execução da validação no aplicativo multiplataforma eShop.
As propriedades do modelo de exibição que exigem validação são do tipo ValidatableObject<T>
e cada instância ValidatableObject<T>
tem regras de validação adicionadas à sua propriedade Validations
. A validação é invocada do modelo de exibição com a chamada do método Validate
da instância ValidatableObject<T>
, que recupera as regras de validação e as executa na propriedade ValidatableObject<T>.Value
. Todos os erros de validação são colocados na propriedade Errors
da instância ValidatableObject<T>
, e a propriedade IsValid
da instância ValidatableObject<T>
é atualizada para indicar se a validação foi bem-sucedida ou falhou. O código abaixo mostra a implementação do ValidatableObject<T>
:
using CommunityToolkit.Mvvm.ComponentModel;
namespace eShop.Validations;
public class ValidatableObject<T> : ObservableObject, IValidity
{
private IEnumerable<string> _errors;
private bool _isValid;
private T _value;
public List<IValidationRule<T>> Validations { get; } = new();
public IEnumerable<string> Errors
{
get => _errors;
private set => SetProperty(ref _errors, value);
}
public bool IsValid
{
get => _isValid;
private set => SetProperty(ref _isValid, value);
}
public T Value
{
get => _value;
set => SetProperty(ref _value, value);
}
public ValidatableObject()
{
_isValid = true;
_errors = Enumerable.Empty<string>();
}
public bool Validate()
{
Errors = Validations
?.Where(v => !v.Check(Value))
?.Select(v => v.ValidationMessage)
?.ToArray()
?? Enumerable.Empty<string>();
IsValid = !Errors.Any();
return IsValid;
}
}
A notificação de alteração de propriedade é fornecida pela classe ObservableObject
e, portanto, um controle Entry
pode associar-se à propriedade IsValid
da instância ValidatableObject<T>
na classe de modelo de exibição para ser notificado sobre a validade dos dados inseridos.
Especificando regras de validação
As regras de validação são especificadas pela criação de uma classe que deriva da interface IValidationRule<T>
, que é mostrada no exemplo de código abaixo:
public interface IValidationRule<T>
{
string ValidationMessage { get; set; }
bool Check(T value);
}
Essa interface especifica que uma classe de regra de validação precisa fornecer um método booliano Check
usado para executar a validação necessária e uma propriedade ValidationMessage
cujo valor é a mensagem de erro de validação que será exibida se a validação falhar.
O exemplo de código a seguir mostra a regra de validação IsNotNullOrEmptyRule<T>
, que é usada para executar a validação do nome de usuário e da senha inseridos pelo usuário no LoginView
ao usar serviços fictícios no aplicativo multiplataforma eShop:
public class IsNotNullOrEmptyRule<T> : IValidationRule<T>
{
public string ValidationMessage { get; set; }
public bool Check(T value) =>
value is string str && !string.IsNullOrWhiteSpace(str);
}
O método Check
retorna um booliano que indica se o argumento de valor é nulo, vazio ou consiste apenas em caracteres de espaço em branco.
Embora não seja usado pelo aplicativo multiplataforma eShop, o exemplo de código a seguir mostra uma regra de validação para validar endereços de email:
public class EmailRule<T> : IValidationRule<T>
{
private readonly Regex _regex = new(@"^([w.-]+)@([w-]+)((.(w){2,3})+)$");
public string ValidationMessage { get; set; }
public bool Check(T value) =>
value is string str && _regex.IsMatch(str);
}
O método Check
retorna um booliano que indica se o argumento de valor é ou não um endereço de email válido. Isso é feito pela pesquisa do argumento de valor para a primeira ocorrência do padrão de expressão regular especificado no construtor Regex
. Se o padrão de expressão regular foi encontrado na cadeia de caracteres de entrada ou não pode ser determinado pela verificação de value
em relação a Regex.IsMatch.
Observação
Às vezes, a validação de propriedade pode envolver propriedades dependentes. Um exemplo de propriedades dependentes é quando o conjunto de valores válidos para a propriedade A depende do valor específico que foi definido na propriedade B. A verificação de se o valor da propriedade A é um dos valores permitidos envolveria a recuperação do valor da propriedade B. Além disso, quando o valor da propriedade B for alterado, a propriedade A precisará ser revalidada.
Adicionando regras de validação a uma propriedade
No aplicativo multiplataforma eShop, as propriedades do modelo de exibição que exigem validação são declaradas como sendo do tipo ValidatableObject<T>
, em que T
é o tipo dos dados a serem validados. O exemplo de código abaixo mostra um exemplo de duas dessas propriedades:
public ValidatableObject<string> UserName { get; private set; }
public ValidatableObject<string> Password { get; private set; }
Para que a validação ocorra, as regras de validação precisam ser adicionadas à coleção Validações de cada instância de ValidatableObject<T>
, conforme demonstrado no exemplo de código abaixo:
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."
});
}
Esse método adiciona a regra de validação IsNotNullOrEmptyRule<T>
à coleção Validations
de cada instância ValidatableObject<T>
, especificando valores para a propriedade ValidationMessage
da regra de validação, que especifica a mensagem de erro de validação exibida se a validação falhar.
Disparando a validação
A abordagem de validação usada no aplicativo multiplataforma eShop pode disparar manualmente a validação de uma propriedade e disparar automaticamente a validação quando uma propriedade é alterada.
Disparando a validação manualmente
A validação pode ser disparada manualmente para uma propriedade de modelo de exibição. Por exemplo, isso ocorre no aplicativo multiplataforma eShop quando o usuário toca no botão Login
no LoginView
, ao usar serviços fictícios. O delegado de comando chama o método MockSignInAsync
em LoginViewModel
, que invoca a validação executando o método Validate
, mostrado no exemplo de código abaixo:
private bool Validate()
{
bool isValidUser = ValidateUserName();
bool isValidPassword = ValidatePassword();
return isValidUser && isValidPassword;
}
private bool ValidateUserName()
{
return _userName.Validate();
}
private bool ValidatePassword()
{
return _password.Validate();
}
O método Validate
executa a validação de nome de usuário e senha inseridos pelo usuário na classe LoginView
, com a invocação do método Validate
em cada instância de ValidatableObject<T>
. O exemplo de código a seguir mostra o método Validate
da classe ValidatableObject<T>
:
public bool Validate()
{
Errors = _validations
?.Where(v => !v.Check(Value))
?.Select(v => v.ValidationMessage)
?.ToArray()
?? Enumerable.Empty<string>();
IsValid = !Errors.Any();
return IsValid;
}
Esse método recupera as regras de validação que foram adicionadas à coleção Validations
do objeto. O método Check
para cada regra de validação recuperada é executado e o valor da propriedade ValidationMessage
para qualquer regra de validação que não valida os dados é adicionada à coleção Errors
da instância ValidatableObject<T>
. Por fim, a propriedade IsValid
é definida e seu valor é retornado ao método de chamada, indicando se a validação foi bem-sucedida ou falhou.
Disparando a validação quando as propriedades são alteradas
A validação também é disparada automaticamente sempre que uma propriedade associada é alterada. Por exemplo, quando uma associação bidirecional na em LoginView
define a propriedade UserName
ou Password
, a validação é disparada. O exemplo de código abaixo demonstra como isso ocorre:
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Behaviors>
<behaviors:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidateUserNameCommand}" />
</Entry.Behaviors>
</Entry>
O controle Entry
associa à propriedade UserName.Value
da instância ValidatableObject<T>
, e a coleção Behaviors
do controle tem uma instância EventToCommandBehavior
adicionada a ela. Esse comportamento executa a classe ValidateUserNameCommand
em resposta ao evento TextChanged
disparado na Entry
, que é gerada quando o texto na Entry
é alterado. Por sua vez, o delegado ValidateUserNameCommand
executa o método ValidateUserName
, que executa o método Validate
na instância ValidatableObject<T>
. Portanto, sempre que o usuário insere um caractere no controle de Entry
como nome de usuário, a validação dos dados inseridos é executada.
Exibindo erros de validação
O aplicativo multiplataforma eShop notifica o usuário sobre qualquer erro de validação realçando o controle que contém os dados inválidos com uma tela de fundo vermelho e exibindo uma mensagem de erro que informa ao usuário por que os dados são inválidos abaixo do controle que contém os dados inválidos. Quando os dados inválidos são corrigidos, a tela de fundo muda de volta para o estado padrão e a mensagem de erro é removida. A imagem abaixo mostra o LoginView
no aplicativo multiplataforma eShop quando erros de validação estão presentes.
Realçando um controle que contém dados inválidos
O .NET MAUI oferece várias maneiras de apresentar informações de validação aos usuários finais, mas uma das maneiras mais diretas é por meio do uso de Triggers
. Triggers
nos fornecem uma maneira de alterar o estado de nossos controles, normalmente aparência, com base em um evento ou alteração de dados que ocorre em um controle. Para validação, usaremos um DataTrigger
que ouvirá as alterações geradas de uma propriedade associada e responderá às alterações. Os controles Entry
em LoginView
são configurados usando o seguinte código:
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Style>
<OnPlatform x:TypeArguments="Style">
<On Platform="iOS, Android" Value="{StaticResource EntryStyle}" />
<On Platform="WinUI" Value="{StaticResource WinUIEntryStyle}" />
</OnPlatform>
</Entry.Style>
<Entry.Behaviors>
<mct:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidateCommand}" />
</Entry.Behaviors>
<Entry.Triggers>
<DataTrigger
TargetType="Entry"
Binding="{Binding UserName.IsValid}"
Value="False">
<Setter Property="BackgroundColor" Value="{StaticResource ErrorColor}" />
</DataTrigger>
</Entry.Triggers>
</Entry>
A classe DataTrigger
especifica as seguintes propriedades:
Propriedade | Descrição |
---|---|
TargetType |
O tipo de controle ao qual o gatilho pertence. |
Binding |
A marcação de Binding de dados que fornecerá notificações de alteração e valor para a condição de gatilho. |
Value |
O valor de dados a ser especificado quando a condição do gatilho é atendida. |
Para essa Entry
, vamos ouvir alterações na propriedade LoginViewModel.UserName.IsValid
. Sempre que essa propriedade gerar uma alteração, o valor será comparado com a propriedade Value
definida no DataTrigger
. Se os valores forem iguais, a condição de gatilho será atendida e os objetos Setter
fornecidos à classe DataTrigger
serão executados. Esse controle tem um único objeto Setter
que atualiza a propriedade BackgroundColor
para uma cor personalizada definida usando a marcação StaticResource
. Quando uma condição Trigger
não for mais atendida, o controle reverterá as propriedades definidas pelo objeto Setter
para seu estado anterior. Para obter mais informações sobre Triggers
, confira Documentação do .NET MAUI: gatilhos.
Exibindo mensagens de erro
A interface do usuário exibe mensagens de erro de validação em controles de rótulo abaixo de cada controle cuja validação de dados falhou. O exemplo de código abaixo mostra o Label
que exibirá uma mensagem de erro de validação se o usuário não tiver inserido um nome de usuário válido:
<Label
Text="{Binding UserName.Errors, Converter={StaticResource FirstValidationErrorConverter}"
Style="{StaticResource ValidationErrorLabelStyle}" />
Cada Rótulo associa-se à propriedade Errors
do objeto de modelo de exibição que está sendo validado. A propriedade Errors
é fornecida pela classe ValidatableObject<T>
e é do tipo IEnumerable<string>
. Como a propriedade Errors
pode conter vários erros de validação, a instância FirstValidationErrorConverter
é usada para recuperar o primeiro erro da coleção para fins de exibição.
Resumo
O aplicativo multiplataforma eShop executa a validação síncrona no lado do cliente das propriedades do modelo de exibição e notifica o usuário sobre quaisquer erros de validação realçando o controle que contém os dados inválidos e exibindo mensagens de erro que informam ao usuário por que os dados são inválidos.
As propriedades do modelo de exibição que exigem validação são do tipo ValidatableObject<T>
e cada instância ValidatableObject<T>
tem regras de validação adicionadas à sua propriedade Validations
. A validação é invocada do modelo de exibição com a chamada do método Validate
da instância ValidatableObject<T>
, que recupera as regras de validação e as executa na propriedade de valor ValidatableObject<T>
. Todos os erros de validação são colocados na propriedade Errors
da instância ValidatableObject<T>
, e a propriedade IsValid da instância ValidatableObject<T>
é atualizada para indicar se a validação foi bem-sucedida ou falhou.