Condividi tramite


Convalida

Suggerimento

Questo contenuto è un estratto dell'eBook, Enterprise Application Patterns Using .NETMAUI, disponibile in .NET Docs o come PDF scaricabile gratuitamente che può essere letto offline.

Enterprise Application Patterns Using .NET MAUI eBook cover thumbnail.

Qualsiasi app che accetta input dagli utenti deve assicurarsi che l'input sia valido. Un'app può, ad esempio, verificare la presenza di input che contiene solo caratteri in un intervallo specifico, è di una determinata lunghezza o corrisponde a un formato specifico. Senza convalida, un utente può fornire dati che causano l'esito negativo dell'app. La convalida corretta applica regole business e può aiutare a impedire a un utente malintenzionato di inserire dati dannosi.

Nel contesto del modello Model-View-ViewModel (MVVM), è spesso necessario un modello di visualizzazione o un modello per eseguire la convalida dei dati e segnalare eventuali errori di convalida alla visualizzazione in modo che l'utente possa correggerli. L'app multipiattaforma eShopOnContainers esegue la convalida sincrona lato client delle proprietà del modello di visualizzazione e notifica all'utente di eventuali errori di convalida evidenziando il controllo contenente i dati non validi e visualizzando messaggi di errore che informano l'utente del motivo per cui i dati non sono validi. L'immagine seguente mostra le classi coinvolte nell'esecuzione della convalida nell'app multipiattaforma eShopOnContainers.

Validation classes in the eShopOnContainers multi-platform app.

Visualizzare le proprietà del modello che richiedono la convalida sono di tipo ValidatableObject<T> e ogni istanza di ValidatableObject<T> ha regole di convalida aggiunte alla relativa proprietà Validations. La convalida viene richiamata dal modello di visualizzazione chiamando il metodo Validate dell'istanza di ValidatableObject<T>, che recupera le regole di convalida e le esegue sulla proprietà ValidatableObject<T>.Value. Eventuali errori di convalida vengono inseriti nella proprietà Errors dell'istanza di ValidatableObject<T> e la proprietà IsValid dell'istanza di ValidatableObject<T> viene aggiornata per indicare se la convalida ha avuto esito positivo o negativo. Il codice seguente illustra l'implementazione del ValidatableObject<T>:

using CommunityToolkit.Mvvm.ComponentModel;
namespace eShopOnContainers.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;
    }
}

La notifica di modifica delle proprietà viene fornita dalla classe ObservableObject e pertanto un controllo Entry può essere associato alla proprietà IsValid dell'istanza di ValidatableObject<T> nella classe del modello di visualizzazione per ricevere una notifica relativa alla validità o meno dei dati immessi.

Specifica delle regole di convalida

Le regole di convalida vengono specificate creando una classe che deriva dall'interfaccia IValidationRule<T>, illustrata nell'esempio di codice seguente:

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

Questa interfaccia specifica che una classe di regole di convalida deve fornire un metodo booleano Check utilizzato per eseguire la convalida richiesta e una proprietà ValidationMessage il cui valore è il messaggio di errore di convalida che verrà visualizzato se la convalida non riesce.

L'esempio di codice seguente illustra la regola di convalida IsNotNullOrEmptyRule<T>, usata per eseguire la convalida del nome utente e della password immessi dall'utente in LoginView quando si usano servizi fittizi nell'app multipiattaforma eShopOnContainers:

public class IsNotNullOrEmptyRule<T> : IValidationRule<T>
{
    public string ValidationMessage { get; set; }

    public bool Check(T value) =>
        value is string str && !string.IsNullOrWhiteSpace(str);
}

Il metodo Check restituisce un valore booleano che indica se l'argomento value è null, vuoto o è costituito solo da spazi vuoti.

Anche se non usato dall'app multipiattaforma eShopOnContainers, l'esempio di codice seguente mostra una regola di convalida per la convalida degli indirizzi di posta elettronica:

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

Il metodo Check restituisce un valore booleano che indica se l'argomento value è un indirizzo di posta elettronica valido. A tale scopo, cercare l'argomento valore per la prima occorrenza del criterio di espressione regolare specificato nel costruttore Regex. Se il criterio di espressione regolare è stato trovato nella stringa di input può essere determinato controllando il value rispetto a Regex.IsMatch.

Nota

La convalida delle proprietà può talvolta comportare proprietà dipendenti. Un esempio di proprietà dipendenti è quando il set di valori validi per la proprietà A dipende dal valore specifico impostato nella proprietà B. Per verificare che il valore della proprietà A sia uno dei valori consentiti comporta il recupero del valore della proprietà B. Inoltre, quando il valore della proprietà B cambia, la proprietà A deve essere riconvalidata.

Aggiunta di regole di convalida a una proprietà

Nell'app multipiattaforma eShopOnContainers visualizzare le proprietà del modello che richiedono la convalida vengono dichiarate come di tipo ValidatableObject<T>, dove T è il tipo dei dati da convalidare. Nell'esempio di codice seguente viene illustrato un esempio di due proprietà di questo tipo:

public ValidatableObject<string> UserName { get; private set; }
public ValidatableObject<string> Password { get; private set; }

Per la convalida, è necessario aggiungere regole di convalida alla raccolta Validations di ogni istanza di ValidatableObject<T>, come illustrato nell'esempio di codice seguente:

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

Questo metodo aggiunge la regola di convalida IsNotNullOrEmptyRule<T> all'insieme Validations di ogni istanza di ValidatableObject<T>, specificando i valori per la proprietà ValidationMessage della regola di convalida, che specifica il messaggio di errore di convalida che verrà visualizzato se la convalida non riesce.

Attivazione della convalida

L'approccio di convalida usato nell'app multipiattaforma eShopOnContainers può attivare manualmente la convalida di una proprietà e attivare automaticamente la convalida quando una proprietà cambia.

Attivazione manuale della convalida

La convalida può essere attivata manualmente per una proprietà del modello di visualizzazione. Ad esempio, questo si verifica nell'app multipiattaforma eShopOnContainers quando l'utente tocca il pulsante Login in LoginView, quando si usano servizi fittizi. Il delegato del comando chiama il metodo MockSignInAsync in LoginViewModel, che richiama la convalida eseguendo il metodo Validate, illustrato nell'esempio di codice seguente:

private bool Validate()
{
    bool isValidUser = ValidateUserName();
    bool isValidPassword = ValidatePassword();
    return isValidUser && isValidPassword;
}

private bool ValidateUserName()
{
    return _userName.Validate();
}

private bool ValidatePassword()
{
    return _password.Validate();
}

Il metodo Validate esegue la convalida del nome utente e della password immessi dall'utente in LoginView, richiamando il metodo Validate in ogni istanza di ValidatableObject<T>. Nell'esempio di codice seguente viene illustrato il metodo Validate della 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;
}

Questo metodo recupera tutte le regole di convalida aggiunte alla raccolta dell'oggetto Validations. Viene eseguito il metodo Check per ogni regola di convalida recuperata e il valore della proprietà ValidationMessage per qualsiasi regola di convalida che non riesce a convalidare i dati viene aggiunto alla raccolta Errors dell'istanza di ValidatableObject<T>. Infine, la proprietà IsValid viene impostata e il relativo valore viene restituito al metodo chiamante, che indica se la convalida è riuscita o non è riuscita.

Attivazione della convalida quando le proprietà cambiano

La convalida viene attivata automaticamente anche ogni volta che viene modificata una proprietà associata. Ad esempio, quando un'associazione bidirezionale in LoginView imposta la proprietà UserName o Password, viene attivata la convalida. Nell'esempio di codice seguente viene illustrato come si verifica questa situazione:

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

Il controllo Entry viene associato alla proprietà UserName.Value dell'istanza di ValidatableObject<T> e all'insieme Behaviors del controllo è stata aggiunta un'istanza di EventToCommandBehavior. Questo comportamento esegue il ValidateUserNameCommand in risposta all'evento di TextChanged generato sul Entry, generato quando il testo in Entry cambia. A sua volta, il delegato ValidateUserNameCommand esegue il metodo ValidateUserName, che esegue il metodo Validate sull'istanza di ValidatableObject<T>. Pertanto, ogni volta che l'utente immette un carattere nel controllo Entry per il nome utente, viene eseguita la convalida dei dati immessi.

Visualizzazione degli errori di convalida

L'app multipiattaforma eShopOnContainers notifica all'utente eventuali errori di convalida evidenziando il controllo che contiene i dati non validi con uno sfondo rosso e visualizzando un messaggio di errore che informa l'utente del motivo per cui i dati non sono validi sotto il controllo contenente i dati non validi. Quando i dati non validi vengono corretti, lo stato in background torna allo stato predefinito e il messaggio di errore viene rimosso. L'immagine seguente mostra LoginView nell'app multipiattaforma eShopOnContainers quando sono presenti errori di convalida.

Displaying validation errors during login.

Evidenziazione di un controllo che contiene dati non validi

.NET MAUI offre diversi modi per presentare informazioni di convalida agli utenti finali, ma uno dei modi più semplici consiste nell'usare Triggers. Triggers consente di modificare lo stato dei controlli, in genere per l'aspetto, in base a un evento o a una modifica dei dati che si verifica per un controllo. Per la convalida, verrà usato un DataTrigger che ascolterà le modifiche generate da una proprietà associata e risponderà alle modifiche. I controlli Entry nel LoginView vengono configurati usando il codice seguente:

<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>

DataTrigger specifica le proprietà seguenti:

Proprietà Descrizione
TargetType Tipo di controllo a cui appartiene il trigger.
Binding Markup dei dati Binding che fornirà notifiche di modifica e valore per la condizione del trigger.
Value Valore dei dati da specificare quando la condizione del trigger è stata soddisfatta.

Per questo Entry, si ascolteranno le modifiche apportate alla proprietà LoginViewModel.UserName.IsValid. Ogni volta che questa proprietà genera una modifica, il valore verrà confrontato con la proprietà Value impostata nel DataTrigger. Se i valori sono uguali, la condizione del trigger verrà soddisfatta e verranno eseguiti tutti gli oggetti Setter forniti al DataTrigger. Questo controllo dispone di un singolo oggetto Setter che aggiorna la proprietà BackgroundColor a un colore personalizzato definito usando il markup StaticResource. Quando una condizione di Trigger non viene più soddisfatta, il controllo ripristina le proprietà impostate dall'oggetto Setter sullo stato precedente. Per ulteriori informazioni su Triggers, consultare .NET MAUI Docs: Trigger.

Visualizzazione di messaggi di errore

L'interfaccia utente visualizza i messaggi di errore di convalida nei controlli Etichetta sotto ogni controllo i cui dati non sono stati convalidati. Nell'esempio di codice seguente viene illustrato il Label che visualizza un messaggio di errore di convalida, se l'utente non ha immesso un nome utente valido:

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

Ogni etichetta viene associata alla proprietà Errors dell'oggetto modello di visualizzazione da convalidare. La proprietà Errors viene fornita dalla classe ValidatableObject<T> ed è di tipo IEnumerable<string>. Poiché la proprietà Errors può contenere più errori di convalida, l'istanza di FirstValidationErrorConverter viene usata per recuperare il primo errore dalla raccolta per la visualizzazione.

Riepilogo

L'app multipiattaforma eShopOnContainers esegue la convalida sincrona lato client delle proprietà del modello di visualizzazione e notifica all'utente di eventuali errori di convalida evidenziando il controllo contenente i dati non validi e visualizzando messaggi di errore che informano l'utente del motivo per cui i dati non sono validi.

Visualizzare le proprietà del modello che richiedono la convalida sono di tipo ValidatableObject<T> e ogni istanza di ValidatableObject<T> ha regole di convalida aggiunte alla relativa proprietà Validations. La convalida viene richiamata dal modello di visualizzazione chiamando il metodo Validate dell'istanza di ValidatableObject<T>, che recupera le regole di convalida e le esegue sulla proprietà di valore ValidatableObject<T>. Eventuali errori di convalida vengono inseriti nella proprietà Errors dell'istanza di ValidatableObject<T> e la proprietà IsValid dell'istanza di ValidatableObject<T> viene aggiornata per indicare se la convalida è riuscita o non è riuscita.