Validação
Gorjeta
Este conteúdo é um excerto do eBook, Enterprise Application Patterns Using .NET MAUI, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.
Qualquer aplicativo que aceite entrada de usuários deve garantir que a entrada seja válida. Um aplicativo pode, por exemplo, verificar se há entradas que contenham apenas caracteres em um intervalo específico, tenham um determinado comprimento ou correspondam 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 injete dados mal-intencionados.
No contexto do padrão Model-View-ViewModel (MVVM), um modelo ou modelo de exibição geralmente será necessário para executar a validação de dados e sinalizar quaisquer erros de validação para a exibição para 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, destacando o controle que contém os dados inválidos e exibindo mensagens de erro que informam o usuário sobre por que os dados são inválidos. A imagem abaixo mostra as classes envolvidas na realizaçã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 ValidatableObject<T>
instância tem regras de validação adicionadas à sua Validations
propriedade. A validação é invocada a partir do modelo de exibição chamando o Validate
método da instância, que recupera as regras de ValidatableObject<T>
validação e as executa na ValidatableObject<T>.Value
propriedade. Quaisquer erros de validação são colocados na Errors
propriedade da ValidatableObject<T>
instância e a IsValid
propriedade da ValidatableObject<T>
instância é atualizada para indicar se a validação foi bem-sucedida ou falhou. O código a seguir 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 ObservableObject
classe e, portanto, um Entry
controle pode se vincular à IsValid
propriedade de instância na classe de modelo de exibição para ser notificado se os dados inseridos ValidatableObject<T>
são válidos ou não.
Especificando regras de validação
As regras de validação são especificadas criando uma classe que deriva da IValidationRule<T>
interface, que é mostrada no exemplo de código a seguir:
public interface IValidationRule<T>
{
string ValidationMessage { get; set; }
bool Check(T value);
}
Essa interface especifica que uma classe de regra de validação deve fornecer um método booleano Check
que é usado para executar a validação necessária e uma ValidationMessage
propriedade 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 senha inseridos pelo usuário no LoginView
ao usar serviços simulados 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 Check
método retorna um booleano indicando se o argumento value é 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 e-mail:
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 Check
método retorna um booleano indicando se o argumento value é ou não um endereço de email válido. Isso é conseguido pesquisando o argumento value para a primeira ocorrência do padrão de expressão regular especificado no Regex
construtor. Se o padrão de expressão regular foi encontrado na cadeia de caracteres de entrada pode ser determinado verificando o value
contra Regex.IsMatch.
Nota
À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. Para verificar se o valor da propriedade A é um dos valores permitidos envolveria recuperar o valor da propriedade B. Além disso, quando o valor do imóvel B muda, o imóvel A precisa ser revalidado.
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 do tipo ValidatableObject<T>
, onde T
é o tipo dos dados a serem validados. O exemplo de código a seguir 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 devem ser adicionadas à coleção Validations de cada ValidatableObject<T>
instância, conforme demonstrado no exemplo de código a seguir:
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 IsNotNullOrEmptyRule<T>
regra de validação à Validations
coleção de cada ValidatableObject<T>
instância, especificando valores para a propriedade da ValidationMessage
regra de validação, que especifica a mensagem de erro de validação que será exibida se a validação falhar.
Acionando a validação
A abordagem de validação usada no aplicativo multiplataforma eShop pode acionar manualmente a validação de uma propriedade e acionar automaticamente a validação quando uma propriedade é alterada.
Acionando a validação manualmente
A validação pode ser acionada manualmente para uma propriedade de modelo de exibição. Por exemplo, isso ocorre no aplicativo multiplataforma eShop quando o usuário toca Login
no botão no LoginView
, ao usar serviços simulados. O delegado de comando chama o MockSignInAsync
método no LoginViewModel
, que invoca a validação executando o Validate
método, que é mostrado no exemplo de código a seguir:
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 Validate
método executa a validação do nome de usuário e senha inseridos pelo usuário no LoginView
, invocando o Validate
método em cada ValidatableObject<T>
instância. O exemplo de código a seguir mostra o Validate
método da ValidatableObject<T>
classe:
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 todas as regras de validação que foram adicionadas à coleção do Validations
objeto. O Check
método para cada regra de validação recuperada é executado e o valor da ValidationMessage
propriedade para qualquer regra de validação que não consegue validar os dados é adicionado à Errors
coleção da ValidatableObject<T>
instância. Finalmente, a IsValid
propriedade é definida e seu valor é retornado para o método de chamada, indicando se a validação foi bem-sucedida ou falhou.
Acionando a validação quando as propriedades são alteradas
A validação também é acionada automaticamente sempre que uma propriedade vinculada é alterada. Por exemplo, quando uma ligação bidirecional no define a propriedade orPassword
, a LoginView
UserName
validação é acionada. O exemplo de código a seguir demonstra como isso ocorre:
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Behaviors>
<behaviors:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidateUserNameCommand}" />
</Entry.Behaviors>
</Entry>
O Entry
controle se liga à UserName.Value
propriedade da ValidatableObject<T>
instância e a coleção do Behaviors
controle tem uma EventToCommandBehavior
instância adicionada a ela. Esse comportamento executa o ValidateUserNameCommand
em resposta ao disparo TextChanged
de evento no Entry
, que é gerado quando o texto nas Entry
alterações. Por sua vez, o ValidateUserNameCommand
delegado executa o ValidateUserName
método, que executa o Validate
método na ValidatableObject<T>
instância. Portanto, toda vez que o usuário insere um caractere no Entry
controle para o nome de usuário, a validação dos dados inseridos é executada.
Exibindo erros de validação
O aplicativo multiplataforma eShop notifica o usuário de quaisquer erros de validação, destacando o controle que contém os dados inválidos com um fundo vermelho e exibindo uma mensagem de erro que informa o 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, o plano 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 do Triggers
. Triggers
Fornecer-nos uma maneira de alterar o estado de nossos controles, normalmente para aparência, com base em um evento ou alteração de dados que ocorre para um controle. Para a validação, usaremos um DataTrigger
que ouvirá as alterações geradas de uma propriedade vinculada e responderá às alterações. Os Entry
controles na LoginView
configuração são 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>
O DataTrigger
especifica as seguintes propriedades:
Property | Description |
---|---|
TargetType |
O tipo de controle ao qual o gatilho pertence. |
Binding |
A marcação de dados Binding que fornecerá notificações de alteração e valor para a condição de gatilho. |
Value |
O valor de dados para especificar quando a condição do gatilho foi atendida. |
Para isso Entry
, estaremos atentos às alterações ao LoginViewModel.UserName.IsValid
imóvel. Cada vez que essa propriedade gerar uma alteração, o valor será comparado com a Value
propriedade definida no DataTrigger
. Se os valores forem iguais, a condição de gatilho DataTrigger
será atendida e todos os Setter
objetos fornecidos ao serão executados. Esse controle tem um único Setter
objeto que atualiza a BackgroundColor
propriedade para uma cor personalizada definida usando a StaticResource
marcação. Quando uma Trigger
condição não for mais atendida, o controle reverterá as propriedades definidas pelo Setter
objeto para seu estado anterior. Para obter mais informações sobre Triggers
o , consulte .NET MAUI Docs: Triggers.
A apresentar mensagens de erro
A interface do usuário exibe mensagens de erro de validação em controles Label abaixo de cada controle cujos dados falharam na validação. O exemplo de código a seguir mostra o Label
que exibe 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 Label se liga à Errors
propriedade do objeto de modelo de exibição que está sendo validado. A Errors
propriedade é fornecida pela ValidatableObject<T>
classe, e é do tipo IEnumerable<string>
. Como a Errors
propriedade pode conter vários erros de validação, a FirstValidationErrorConverter
instância é usada para recuperar o primeiro erro da coleção para exibição.
Resumo
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, destacando o controle que contém os dados inválidos e exibindo mensagens de erro que informam o 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 ValidatableObject<T>
instância tem regras de validação adicionadas à sua Validations
propriedade. A validação é invocada a partir do modelo de exibição chamando o Validate
método da instância, que recupera as regras de ValidatableObject<T>
validação e as executa na ValidatableObject<T>
propriedade Value. Quaisquer erros de validação são colocados na propriedade da ValidatableObject<T>
instância e a propriedade IsValid da instância é atualizada para indicar se a Errors
ValidatableObject<T>
validação foi bem-sucedida ou falhou.