Convalida del modello in ASP.NET Core MVC e Razor pagine

Questo articolo illustra come convalidare l'input dell'utente in un'app MVC o Razor Pages core ASP.NET.

Visualizzare o scaricare il codice di esempio (procedura per il download).

Stato del modello

Lo stato del modello rappresenta gli errori che provengono da due sottosistemi: associazione di modelli e convalida del modello. Gli errori originati dall'associazione di modelli sono in genere errori di conversione dei dati. Ad esempio, un valore "x" viene immesso in un campo integer. La convalida del modello viene eseguita dopo l'associazione di modelli e segnala gli errori in cui i dati non sono conformi alle regole business. Ad esempio, un valore 0 viene immesso in un campo che prevede una classificazione compresa tra 1 e 5.

Sia l'associazione del modello che la convalida del modello vengono eseguite prima dell'esecuzione di un'azione del controller o di un Razor metodo del gestore Pages. Per le app Web, è responsabilità dell'app esaminare ModelState.IsValid e rispondere nel modo appropriato. Le app Web in genere redisplayno la pagina con un messaggio di errore, come illustrato nell'esempio pages seguente Razor :

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Per ASP.NET Core MVC con controller e visualizzazioni, nell'esempio seguente viene illustrato come controllare ModelState.IsValid l'interno di un'azione del controller:

public async Task<IActionResult> Create(Movie movie)
{
    if (!ModelState.IsValid)
    {
        return View(movie);
    }

    _context.Movies.Add(movie);
    await _context.SaveChangesAsync();

    return RedirectToAction(nameof(Index));
}

I controller API Web non devono verificare ModelState.IsValid se hanno l'attributo [ApiController]. In tal caso, viene restituita una risposta HTTP 400 automatica contenente i dettagli dell'errore quando lo stato del modello non è valido. Per altre informazioni, vedere Risposte HTTP 400 automatiche.

Rieseguire la convalida

La convalida è automatica, ma potrebbe essere necessario ripeterla manualmente. Ad esempio, è possibile che si calcoli un valore per una proprietà e che si voglia rieseguire la convalida dopo aver impostato la proprietà sul valore calcolato. Per rieseguire la convalida, chiamare ModelStateDictionary.ClearValidationState per cancellare la convalida specifica del modello da convalidare seguito da TryValidateModel:

public async Task<IActionResult> OnPostTryValidateAsync()
{
    var modifiedReleaseDate = DateTime.Now.Date;
    Movie.ReleaseDate = modifiedReleaseDate;

    ModelState.ClearValidationState(nameof(Movie));
    if (!TryValidateModel(Movie, nameof(Movie)))
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Attributi di convalida

Gli attributi di convalida consentono di specificare le regole di convalida per le proprietà del modello. Nell'esempio seguente dell'app di esempio viene illustrata una classe di modello annotata con attributi di convalida. L'attributo [ClassicMovie] è un attributo di convalida personalizzato e gli altri sono incorporati. Non visualizzato è [ClassicMovieWithClientValidator], che mostra un modo alternativo per implementare un attributo personalizzato.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Attributi predefiniti

Di seguito sono elencati alcuni degli attributi di convalida predefiniti:

  • [ValidateNever]: indica che una proprietà o un parametro deve essere escluso dalla convalida.
  • [CreditCard]: convalida che la proprietà abbia un formato di carta di credito. Richiede metodi aggiuntivi per la convalida di jQuery.
  • [Confronto]: convalida che due proprietà in un modello corrispondano.
  • [EmailAddress]: convalida che la proprietà abbia un formato di posta elettronica.
  • [Telefono]: convalida che la proprietà abbia un formato di numero di telefono.
  • [Intervallo]: convalida che il valore della proprietà rientra in un intervallo specificato.
  • [RegularExpression]: convalida che il valore della proprietà corrisponda a un'espressione regolare specificata.
  • [Obbligatorio]: convalida che il campo non sia Null. Per informazioni dettagliate sul comportamento di questo attributo, vedere [Required] attributo .
  • [StringLength]: verifica che un valore della proprietà stringa non superi un limite di lunghezza specificato.
  • [Url]: convalida che la proprietà abbia un formato URL.
  • [Remoto]: convalida l'input nel client chiamando un metodo di azione nel server. Per informazioni dettagliate sul comportamento di questo attributo, vedere [Remote] attributo .

Un elenco completo degli attributi di convalida è disponibile nello spazio dei System.ComponentModel.DataAnnotations nomi .

messaggi di errore

Gli attributi di convalida consentono di specificare il messaggio di errore da visualizzare in caso di input non valido. Ad esempio:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

Internamente gli attributi chiamano String.Format con un segnaposto per il nome campo e talvolta con segnaposto aggiuntivi. Ad esempio:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

Quando applicato a una proprietà Name, il messaggio di errore creato con il codice precedente sarà "La lunghezza del nome utente deve essere compresa tra 6 e 8".

Per scoprire i parametri passati a String.Format per un messaggio di errore dell'attributo specifico, vedere il codice sorgente DataAnnotations.

Usare JSi nomi delle proprietà ON negli errori di convalida

Per impostazione predefinita, quando si verifica un errore di convalida, la convalida del modello produce un ModelStateDictionary oggetto con il nome della proprietà come chiave di errore. Alcune app, ad esempio le app a pagina singola, traggono vantaggio dall'uso JSdei nomi delle proprietà ON per gli errori di convalida generati dalle API Web. Il codice seguente configura la convalida per l'utilizzo di per l'utilizzo SystemTextJsonValidationMetadataProvider dei JSnomi delle proprietà ON:

using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());
});

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Il codice seguente configura la convalida per l'utilizzo di per l'utilizzo NewtonsoftJsonValidationMetadataProviderJSdel nome della proprietà ON quando si usa Json.NET:

using Microsoft.AspNetCore.Mvc.NewtonsoftJson;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Per un esempio dei criteri per l'uso di camel-casing, vedere Program.cs in GitHub.

Tipi di riferimento non nullable e attributo [Obbligatorio]

Il sistema di convalida considera parametri non nullable o proprietà associate come se avessero un [Required(AllowEmptyStrings = true)] attributo. Abilitando Nullablei contesti, MVC inizia implicitamente a convalidare proprietà o parametri non nullable come se fossero stati attribuiti con l'attributo [Required(AllowEmptyStrings = true)] . Osservare il codice seguente:

public class Person
{
    public string Name { get; set; }
}

Se l'app è stata compilata con <Nullable>enable</Nullable>, un valore mancante per Name in un JSpost ON o form genera un errore di convalida. Questo può sembrare contraddittorio perché l'attributo [Required(AllowEmptyStrings = true)] è implicito, ma questo comportamento è previsto perché le stringhe vuote vengono convertite in null per impostazione predefinita. Usare un tipo riferimento nullable per consentire l'impostazione di valori Null o mancanti per la Name proprietà :

public class Person
{
    public string? Name { get; set; }
}

Questo comportamento può essere disabilitato configurando SuppressImplicitRequiredAttributeForNonNullableReferenceTypes in Program.cs:

builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

Convalida dell'attributo [Required] nel server

Nel server un valore obbligatorio viene considerato mancante se la proprietà è Null. Un campo non nullable è sempre valido e il [Required] messaggio di errore dell'attributo non viene mai visualizzato.

Può però succedere che l'associazione di modelli per una proprietà non nullable abbia esito negativo, generando un messaggio di errore, ad esempio The value '' is invalid. Per specificare un messaggio di errore personalizzato per la convalida lato server di tipi non nullable, sono disponibili le opzioni seguenti:

  • Il campo deve ammettere i valori Null, ad esempio decimal? anziché decimal. I tipi valore T> nullable<vengono considerati come tipi nullable standard.

  • Specificare il messaggio di errore predefinito che l'associazione di modelli deve usare, come illustrato nell'esempio seguente:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

    Per altre informazioni sugli errori dell'associazione di modelli che possono essere impostati come messaggi predefiniti, vedere DefaultModelBindingMessageProvider.

Convalida dell'attributo [Required] nel client

Le stringhe e i tipi non nullable sono gestiti in modo diverso nel client rispetto a come vengono gestiti nel server. Nel client:

  • Un valore viene considerato presente solo viene immesso un input per tale valore. La convalida lato client gestisce quindi i tipi non nullable come i tipi nullable.
  • Lo spazio vuoto in un campo stringa viene considerato un input valido per il metodo required della convalida di jQuery. La convalida lato server considera un campo stringa obbligatorio non valido solo se viene immesso uno spazio vuoto.

Come indicato in precedenza, i tipi non nullable vengono trattati come se avessero un attributo [Required(AllowEmptyStrings = true)]. Quindi viene eseguita la convalida lato client anche se non si applica l'attributo [Required(AllowEmptyStrings = true)]. Se invece non si usa l'attributo, viene visualizzato un messaggio di errore predefinito. Per specificare un messaggio di errore personalizzato, usare l'attributo.

Attributo [Remote]

L'attributo [Remote] implementa la convalida lato client che richiede la chiamata di un metodo nel server per determinare se l'input del campo è valido. Ad esempio, l'app potrebbe dover verificare se un nome utente è già in uso.

Per implementare la convalida remota:

  1. Creare un metodo di azione per JavaScript da chiamare. Il metodo remoto jQuery Validation prevede una JSrisposta ON:

    • true indica che i dati di input sono validi.
    • false, undefined o null indica che l'input non è valido. Visualizzare il messaggio di errore predefinito.
    • Qualsiasi altra stringa indica che l'input non è valido. Visualizzare la stringa come messaggio di errore personalizzato.

    Di seguito è riportato un esempio di un metodo di azione che restituisce un messaggio di errore personalizzato:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. Nella classe del modello annotare la proprietà con un attributo [Remote] che punta al metodo di azione di convalida, come illustrato nell'esempio seguente:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; } = null!;
    

La convalida lato server deve essere implementata anche per i client che hanno disabilitato JavaScript.

Campi aggiuntivi

La proprietà [Remote] dell'attributo AdditionalFields consente di convalidare combinazioni di campi rispetto ai dati nel server. Ad esempio, se il modello User avesse le proprietà FirstName e LastName, potrebbe essere necessario controllare che non siano già esistenti utenti con la stessa coppia di nomi. Nell'esempio riportato di seguito viene illustrato come usare AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;

AdditionalFieldspotrebbe essere impostato in modo esplicito sulle stringhe "FirstName" e "LastName", ma l'uso dell'operatore nameof semplifica il refactoring successivo. Il metodo di azione per questa convalida deve accettare entrambi firstName gli argomenti e lastName :

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userService.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

Quando si immette un nome o un cognome, JavaScript esegue una chiamata remota per verificare se tale coppia di nomi è in uso.

Per convalidare due o più campi aggiuntivi, specificarli sotto forma di elenco delimitato da virgole. Ad esempio, per aggiungere una proprietà MiddleName al modello, impostare l'attributo [Remote] come illustrato nell'esempio seguente:

[Remote(action: "VerifyName", controller: "Users",
    AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields, come tutti gli argomenti dell'attributo, deve essere un'espressione costante. Non usare quindi una stringa interpolata oppure chiamare Join per inizializzare AdditionalFields.

Alternative agli attributi predefiniti

Se si vuole usare un tipo di convalida non definita da attributi predefiniti, è possibile eseguire le operazioni seguenti:

Attributi personalizzati

Per gli scenari non gestiti dagli attributi di convalida predefiniti, è possibile creare attributi di convalida personalizzati. Creare una classe che eredita da ValidationAttribute ed eseguire l'override del metodo IsValid.

Il metodo IsValid accetta un oggetto denominato value, ovvero l'input da convalidare. Un overload accetta anche un oggetto ValidationContext, che contiene informazioni aggiuntive, ad esempio l'istanza del modello creato dall'associazione di modelli.

Nell'esempio seguente si convalida che la data di uscita di un film di genere Classic non sia successiva a un anno specifico. Attributo [ClassicMovie] :

  • Viene eseguito solo nel server.
  • Per i film classici, convalida la data di rilascio:
public class ClassicMovieAttribute : ValidationAttribute
{
    public ClassicMovieAttribute(int year)
        => Year = year;

    public int Year { get; }

    public string GetErrorMessage() =>
        $"Classic movies must have a release year no later than {Year}.";

    protected override ValidationResult? IsValid(
        object? value, ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value!).Year;

        if (movie.Genre == Genre.Classic && releaseYear > Year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }
}

La variabile movie nell'esempio precedente rappresenta un oggetto Movie contenente i dati dell'invio del modulo. Se la convalida ha esito negativo, viene restituito un codice ValidationResult con un messaggio di errore.

IValidatableObject

L'esempio precedente usa solo tipi Movie. Un'altra opzione per la convalida a livello di classe consiste nell'implementare IValidatableObject nella classe del modello, come illustrato nell'esempio seguente:

public class ValidatableMovie : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year no later than {_classicYear}.",
                new[] { nameof(ReleaseDate) });
        }
    }
}

Convalida personalizzata

Il codice seguente illustra come aggiungere un errore del modello dopo aver esaminato il modello:

if (Contact.Name == Contact.ShortName)
{
    ModelState.AddModelError("Contact.ShortName", 
                             "Short name can't be the same as Name.");
}

Il codice seguente implementa il test di convalida in un controller:

if (contact.Name == contact.ShortName)
{
    ModelState.AddModelError(nameof(contact.ShortName),
                             "Short name can't be the same as Name.");
}

Il codice seguente verifica il numero di telefono e l'indirizzo di posta elettronica sono univoci:

public async Task<IActionResult> OnPostAsync()
{
    // Attach Validation Error Message to the Model on validation failure.          

    if (Contact.Name == Contact.ShortName)
    {
        ModelState.AddModelError("Contact.ShortName", 
                                 "Short name can't be the same as Name.");
    }

    if (_context.Contact.Any(i => i.PhoneNumber == Contact.PhoneNumber))
    {
        ModelState.AddModelError("Contact.PhoneNumber",
                                  "The Phone number is already in use.");
    }
    if (_context.Contact.Any(i => i.Email == Contact.Email))
    {
        ModelState.AddModelError("Contact.Email", "The Email is already in use.");
    }

    if (!ModelState.IsValid || _context.Contact == null || Contact == null)
    {
        // if model is invalid, return the page with the model state errors.
        return Page();
    }
    _context.Contact.Add(Contact);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Il codice seguente implementa il test di convalida in un controller:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,ShortName,Email,PhoneNumber")] Contact contact)
{
    // Attach Validation Error Message to the Model on validation failure.
    if (contact.Name == contact.ShortName)
    {
        ModelState.AddModelError(nameof(contact.ShortName),
                                 "Short name can't be the same as Name.");
    }

    if (_context.Contact.Any(i => i.PhoneNumber == contact.PhoneNumber))
    {
        ModelState.AddModelError(nameof(contact.PhoneNumber),
                                  "The Phone number is already in use.");
    }
    if (_context.Contact.Any(i => i.Email == contact.Email))
    {
        ModelState.AddModelError(nameof(contact.Email), "The Email is already in use.");
    }

    if (ModelState.IsValid)
    {
        _context.Add(contact);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    return View(contact);
}

Il controllo di un numero di telefono o di un messaggio di posta elettronica univoco viene in genere eseguito anche con la convalida remota.

Validationresult

Si consideri il codice personalizzato ValidateNameAttributeseguente:

public class ValidateNameAttribute : ValidationAttribute
{
    public ValidateNameAttribute()
    {
        const string defaultErrorMessage = "Error with Name";
        ErrorMessage ??= defaultErrorMessage;
    }

    protected override ValidationResult? IsValid(object? value,
                                         ValidationContext validationContext)
    {
        if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
        {
            return new ValidationResult("Name is required.");
        }

        if (value.ToString()!.ToLower().Contains("zz"))
        {

            return new ValidationResult(
                        FormatErrorMessage(validationContext.DisplayName));
        }

        return ValidationResult.Success;
    }
}

Nel codice seguente viene applicato l'attributo personalizzato [ValidateName] :

public class Contact
{
    public Guid Id { get; set; }

    [ValidateName(ErrorMessage = "Name must not contain `zz`")] 
    public string? Name { get; set; }
    public string? Email { get; set; }
    public string? PhoneNumber { get; set; }
}

Quando il modello contiene zz, viene restituito un nuovo ValidationResult oggetto .

Convalida del nodo di primo livello

I nodi di primo livello includono:

  • Parametri azione
  • Proprietà del controller
  • Parametri del gestore di pagina
  • Proprietà del modello di pagina

Vengono convalidati i nodi di primo livello associati al modello, oltre a convalidare le proprietà del modello. Nell'esempio seguente dell'app di esempio il VerifyPhone metodo usa per RegularExpressionAttribute convalidare il phone parametro action:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

I nodi di primo livello possono usare BindRequiredAttribute con gli attributi di convalida. Nell'esempio seguente dell'app di esempio, il CheckAge metodo specifica che il age parametro deve essere associato dalla stringa di query quando viene inviato il modulo:

[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{

Nella pagina Check Age (CheckAge.cshtml) sono disponibili due moduli. Il primo modulo invia un Age valore di come parametro della stringa di 99 query: https://localhost:5001/Users/CheckAge?Age=99.

Quando viene inviato un parametro correttamente formattato age dalla stringa di query, il modulo viene convalidato.

Il secondo modulo nella pagina Check Age invia il valore Age nel corpo della richiesta e la convalida ha esito negativo. L'associazione non riesce perché il parametro age deve provenire da una stringa di query.

Numero massimo di errori

La convalida si interrompe quando viene raggiunto il numero di errori (200 per impostazione predefinita). È possibile configurare questo numero con il codice seguente in Program.cs:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

builder.Services.AddSingleton
    <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();

Numero massimo di ricorsioni

ValidationVisitor attraversare l'oggetto grafico del modello che deve essere convalidato. Per i modelli profondi o ricorsivi infinitamente, la convalida può comportare un overflow dello stack. MvcOptions.MaxValidationDepth consente di interrompere la convalida in anticipo se la ricorsione del visitatore supera una profondità configurata. Il valore predefinito di MvcOptions.MaxValidationDepth è 32.

Corto circuito automatico

La convalida viene ignorata automaticamente (corto circuito) se l'oggetto grafico non richiede la convalida. Gli oggetti per cui viene ignorata la convalida sono le raccolte di primitive (ad esempio byte[], string[], Dictionary<string, string>) e gli oggetti grafici complessi che non hanno validator.

Convalida lato client

La convalida lato client non consente l'invio del modulo finché non è ritenuto valido. Tramite il pulsante Invia viene eseguito JavaScript, che invia il modulo oppure visualizza i messaggi di errore.

La convalida lato client evitare un inutile round trip al server in caso di errori di input in un modulo. I riferimenti di script seguenti in _Layout.cshtml e _ValidationScriptsPartial.cshtml supportano la convalida lato client:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>

Lo script jQuery Unobtrusive Validation è una libreria front-end Microsoft personalizzata basata sul popolare plug-in jQuery Validation . Senza Query Unobtrusive Validation, sarebbe necessario scrivere il codice della stessa logica di convalida in due posizioni, vale a dire negli attributi di convalida lato server nelle proprietà del modello e nuovamente negli script lato client. Invece, gli helper tage agli helper HTML usano gli attributi di convalida e i metadati di tipo delle proprietà del modello per eseguire il rendering degli attributi data- HTML 5 negli elementi del modulo che devono essere convalidati. jQuery Unobtrusive Validation analizza gli data- attributi e passa la logica alla convalida jQuery, "copiando" in modo efficace la logica di convalida lato server nel client. È possibile visualizzare gli errori di convalida nel client tramite gli helper tag, come illustrato di seguito:

<div class="form-group">
    <label asp-for="Movie.ReleaseDate" class="control-label"></label>
    <input asp-for="Movie.ReleaseDate" class="form-control" />
    <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>

Gli helper tag precedenti eseguono il rendering del codice HTML seguente:

<div class="form-group">
    <label class="control-label" for="Movie_ReleaseDate">Release Date</label>
    <input class="form-control" type="date" data-val="true"
        data-val-required="The Release Date field is required."
        id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
    <span class="text-danger field-validation-valid"
        data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>

Si noti che gli attributi data- nell'output HTML corrispondono agli attributi di convalida per la proprietà Movie.ReleaseDate. L'attributo data-val-required contiene un messaggio di errore che viene visualizzato se l'utente non compila il campo relativo alla data di uscita. JQuery Unobtrusive Validation passa questo valore al metodo jQuery Validation required(), che quindi visualizza il messaggio nell'elemento span> a <cui si accompagna.

La convalida dei tipi di dati è basata sul tipo .NET di una proprietà, a meno che non venga sottoposto a override da un attributo [DataType]. I browser generano messaggi di errore predefiniti propri che il pacchetto jQuery Validation Unobtrusive Validation può comunque sostituire. [DataType] attributi e sottoclassi, ad esempio [EmailAddress] consentono di specificare il messaggio di errore.

Convalida non invasiva

Per informazioni sulla convalida non invasiva, vedere questo problema di GitHub.

Aggiungere la convalida a moduli dinamici

JQuery Unobtrusive Validation passa la logica di convalida e i parametri alla convalida jQuery al primo caricamento della pagina. La convalida non viene quindi eseguita automaticamente nei moduli generati in modo dinamico. Per abilitare la convalida, chiedere a jQuery Unobtrusive Validation di analizzare il modulo dinamico immediatamente dopo essere stato creato. Il codice riportato di seguito configura ad esempio la convalida lato client in un modulo che è stato aggiunto tramite AJAX.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Il metodo $.validator.unobtrusive.parse() accetta un selettore jQuery per ogni argomento. Questo metodo indica a jQuery Unobtrusive Validation di analizzare gli attributi data- dei moduli all'interno di tale tipo di selettore. I valori di questi attributi vengono quindi passati al plug-in jQuery Validation.

Aggiungere la convalida a controlli dinamici

Il metodo $.validator.unobtrusive.parse() funziona su un intero modulo e non sui singoli controlli generati dinamicamente, ad esempio <input> e <select/>. Per eseguire il reparse del modulo, rimuovere i dati di convalida aggiunti quando il modulo è stato precedentemente analizzato, come illustrato nell'esempio seguente:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validation
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Convalida lato client personalizzata

La convalida lato client personalizzata viene eseguita generando data- attributi HTML che funzionano con un adattatore di convalida jQuery personalizzato. Il codice dell'adapter di esempio seguente è stato scritto per gli attributi [ClassicMovie] e [ClassicMovieWithClientValidator] presentati precedentemente nell'articolo:

$.validator.addMethod('classicmovie', function (value, element, params) {
    var genre = $(params[0]).val(), year = params[1], date = new Date(value);

    // The Classic genre has a value of '0'.
    if (genre && genre.length > 0 && genre[0] === '0') {
        // The release date for a Classic is valid if it's no greater than the given year.
        return date.getUTCFullYear() <= year;
    }

    return true;
});

$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
    var element = $(options.form).find('select#Movie_Genre')[0];

    options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
    options.messages['classicmovie'] = options.message;
});

Per informazioni su come scrivere adapter, vedere la documentazione relativa alla convalida di jQuery.

L'uso di un adapter per un determinato campo viene attivato dagli attributi data-, i quali eseguono le operazioni seguenti:

  • Contrassegnano il campo come campo da sottoporre a convalida (data-val="true").
  • Identificano un nome della regola di convalida e un testo del messaggio di errore, ad esempio data-val-rulename="Error message.".
  • Specificare i parametri aggiuntivi necessari al validator, ad esempio data-val-rulename-param1="value".

L'esempio seguente mostra gli data- attributi per l'attributo dell'app diClassicMovie esempio:

<input class="form-control" type="date"
    data-val="true"
    data-val-classicmovie="Classic movies must have a release year no later than 1960."
    data-val-classicmovie-year="1960"
    data-val-required="The Release Date field is required."
    id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">

Come già accennato in precedenza, gli helper tag e gli helper HTML usano le informazioni degli attributi di convalida per eseguire il rendering degli attributi data-. Per scrivere il codice che crea attributi HTML data- personalizzati, sono disponibili due opzioni:

  • Creare una classe che deriva da AttributeAdapterBase<TAttribute> e una classe che implementa IValidationAttributeAdapterProvider e registrare l'attributo e il relativo adapter nell'inserimento delle dipendenze. Questo metodo segue il principio di responsabilità singola in quanto il codice di convalida correlato al server e correlato al client si trova in classi separate. L'adattatore ha anche il vantaggio che, dal momento che è registrato in inserimento di dipendenze, altri servizi in di inserimento di dipendenze sono disponibili, se necessario.
  • Implementare IClientModelValidator nella classe ValidationAttribute. Questo metodo potrebbe essere appropriato se l'attributo non esegue la convalida lato server e non richiede altri servizi dell'inserimento delle dipendenze.

AttributeAdapter per la convalida lato client

Questo metodo di rendering data- degli attributi in HTML viene usato dall'attributo nell'appClassicMoviedi esempio. Per aggiungere la convalida lato client usando questo metodo:

  1. Creare una classe di adapter dell'attributo per l'attributo di convalida personalizzata. Derivare la classe da AttributeAdapterBase<TAttribute>. Creare un metodo AddValidation che aggiunge gli attributi data- all'output sottoposto a rendering, come illustrato in questo esempio:

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        public ClassicMovieAttributeAdapter(
            ClassicMovieAttribute attribute, IStringLocalizer? stringLocalizer)
            : base(attribute, stringLocalizer)
        {
    
        }
    
        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public override string GetErrorMessage(ModelValidationContextBase validationContext)
            => Attribute.GetErrorMessage();
    }
    
  2. Creare una classe di provider dell'adapter che implementa IValidationAttributeAdapterProvider. Nel metodo GetAttributeAdapter passare l'attributo personalizzato al costruttore dell'adapter, come illustrato in questo esempio:

    public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter? GetAttributeAdapter(
            ValidationAttribute attribute, IStringLocalizer? stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
    
            return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    
  3. Registrare il provider dell'adapter per l'inserimento delle dipendenze in Program.cs:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator per la convalida lato client

Questo metodo di rendering data- degli attributi in HTML viene usato dall'attributo nell'appClassicMovieWithClientValidatordi esempio. Per aggiungere la convalida lato client usando questo metodo:

  • Nell'attributo di convalida personalizzato implementare l'interfaccia IClientModelValidator e creare un metodo AddValidation. Nel metodo AddValidation aggiungere gli attributi data- per la convalida, come illustrato nell'esempio seguente:

    public class ClassicMovieWithClientValidatorAttribute :
        ValidationAttribute, IClientModelValidator
    {
        public ClassicMovieWithClientValidatorAttribute(int year)
            => Year = year;
    
        public int Year { get; }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public string GetErrorMessage() =>
            $"Classic movies must have a release year no later than {Year}.";
    
        protected override ValidationResult? IsValid(
            object? value, ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value!).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > Year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
    }
    

Disabilitare la convalida lato client

Il codice seguente disabilita la convalida client in Razor Pages:

builder.Services.AddRazorPages()
    .AddViewOptions(options =>
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    });

Altre opzioni per disabilitare la convalida lato client:

  • Impostare come commento il riferimento a _ValidationScriptsPartial in tutti i .cshtml file.
  • Rimuovere il contenuto del file Pages\Shared_ValidationScriptsPartial.cshtml .

L'approccio precedente non impedisce la convalida lato client di ASP.NET libreria di classi core IdentityRazor . Per altre informazioni, vedere Scaffolding Identity nei progetti ASP.NET Core.

Dettagli del problema

I dettagli del problema non sono l'unico formato di risposta per descrivere un errore dell'API HTTP, ma vengono comunemente usati per segnalare errori per le API HTTP.

Il servizio dettagli problema implementa l'interfaccia , che supporta la IProblemDetailsService creazione dei dettagli del problema in ASP.NET Core. Il AddProblemDetails metodo di estensione in IServiceCollection registra l'implementazione predefinita IProblemDetailsService .

In ASP.NET app Core, il middleware seguente genera risposte HTTP con dettagli del problema quando AddProblemDetails viene chiamato, tranne quando l'intestazione HTTP della Accept richiesta non include uno dei tipi di contenuto supportati dal registrato IProblemDetailsWriter (impostazione predefinita: application/json):

Risorse aggiuntive

Questo articolo illustra come convalidare l'input dell'utente in un'app MVC o Razor Pages core ASP.NET.

Visualizzare o scaricare il codice di esempio (procedura per il download).

Stato del modello

Lo stato del modello rappresenta gli errori che provengono da due sottosistemi: associazione di modelli e convalida del modello. Gli errori originati dall'associazione di modelli sono in genere errori di conversione dei dati. Ad esempio, un valore "x" viene immesso in un campo integer. La convalida del modello viene eseguita dopo l'associazione di modelli e segnala gli errori in cui i dati non sono conformi alle regole business. Ad esempio, un valore 0 viene immesso in un campo che prevede una classificazione compresa tra 1 e 5.

Sia l'associazione del modello che la convalida del modello vengono eseguite prima dell'esecuzione di un'azione del controller o di un Razor metodo del gestore Pages. Per le app Web, è responsabilità dell'app esaminare ModelState.IsValid e rispondere nel modo appropriato. Le app Web in genere redisplayno la pagina con un messaggio di errore, come illustrato nell'esempio pages seguente Razor :

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Per ASP.NET Core MVC con controller e visualizzazioni, nell'esempio seguente viene illustrato come controllare ModelState.IsValid l'interno di un'azione del controller:

public async Task<IActionResult> Create(Movie movie)
{
    if (!ModelState.IsValid)
    {
        return View(movie);
    }

    _context.Movies.Add(movie);
    await _context.SaveChangesAsync();

    return RedirectToAction(nameof(Index));
}

I controller API Web non devono verificare ModelState.IsValid se hanno l'attributo [ApiController]. In tal caso, viene restituita una risposta HTTP 400 automatica contenente i dettagli dell'errore quando lo stato del modello non è valido. Per altre informazioni, vedere Risposte HTTP 400 automatiche.

Rieseguire la convalida

La convalida è automatica, ma potrebbe essere necessario ripeterla manualmente. Ad esempio, è possibile che si calcoli un valore per una proprietà e che si voglia rieseguire la convalida dopo aver impostato la proprietà sul valore calcolato. Per rieseguire la convalida, chiamare ModelStateDictionary.ClearValidationState per cancellare la convalida specifica del modello da convalidare seguito da TryValidateModel:

public async Task<IActionResult> OnPostTryValidateAsync()
{
    var modifiedReleaseDate = DateTime.Now.Date;
    Movie.ReleaseDate = modifiedReleaseDate;

    ModelState.ClearValidationState(nameof(Movie));
    if (!TryValidateModel(Movie, nameof(Movie)))
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Attributi di convalida

Gli attributi di convalida consentono di specificare le regole di convalida per le proprietà del modello. Nell'esempio seguente dell'app di esempio viene illustrata una classe di modello annotata con attributi di convalida. L'attributo [ClassicMovie] è un attributo di convalida personalizzato e gli altri sono incorporati. Non visualizzato è [ClassicMovieWithClientValidator], che mostra un modo alternativo per implementare un attributo personalizzato.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Attributi predefiniti

Di seguito sono elencati alcuni degli attributi di convalida predefiniti:

  • [ValidateNever]: indica che una proprietà o un parametro deve essere escluso dalla convalida.
  • [CreditCard]: convalida che la proprietà abbia un formato di carta di credito. Richiede metodi aggiuntivi per la convalida di jQuery.
  • [Confronto]: convalida che due proprietà in un modello corrispondano.
  • [EmailAddress]: convalida che la proprietà abbia un formato di posta elettronica.
  • [Telefono]: convalida che la proprietà abbia un formato di numero di telefono.
  • [Intervallo]: convalida che il valore della proprietà rientra in un intervallo specificato.
  • [RegularExpression]: convalida che il valore della proprietà corrisponda a un'espressione regolare specificata.
  • [Obbligatorio]: convalida che il campo non sia Null. Per informazioni dettagliate sul comportamento di questo attributo, vedere [Required] attributo .
  • [StringLength]: verifica che un valore della proprietà stringa non superi un limite di lunghezza specificato.
  • [Url]: convalida che la proprietà abbia un formato URL.
  • [Remoto]: convalida l'input nel client chiamando un metodo di azione nel server. Per informazioni dettagliate sul comportamento di questo attributo, vedere [Remote] attributo .

Un elenco completo degli attributi di convalida è disponibile nello spazio dei System.ComponentModel.DataAnnotations nomi .

messaggi di errore

Gli attributi di convalida consentono di specificare il messaggio di errore da visualizzare in caso di input non valido. Ad esempio:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

Internamente gli attributi chiamano String.Format con un segnaposto per il nome campo e talvolta con segnaposto aggiuntivi. Ad esempio:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

Quando applicato a una proprietà Name, il messaggio di errore creato con il codice precedente sarà "La lunghezza del nome utente deve essere compresa tra 6 e 8".

Per scoprire i parametri passati a String.Format per un messaggio di errore dell'attributo specifico, vedere il codice sorgente DataAnnotations.

Tipi di riferimento non nullable e attributo [Obbligatorio]

Il sistema di convalida considera parametri non nullable o proprietà associate come se avessero un [Required(AllowEmptyStrings = true)] attributo. Abilitando i contesti, MVC inizia implicitamente a convalidare proprietà non nullable su tipi o parametri non generici come se fossero stati attribuiti con l'attributo [Required(AllowEmptyStrings = true)] .Nullable Osservare il codice seguente:

public class Person
{
    public string Name { get; set; }
}

Se l'app è stata compilata con <Nullable>enable</Nullable>, un valore mancante per Name in un JSpost ON o form genera un errore di convalida. Usare un tipo riferimento nullable per consentire l'impostazione di valori Null o mancanti per la Name proprietà :

public class Person
{
    public string? Name { get; set; }
}

Questo comportamento può essere disabilitato configurando SuppressImplicitRequiredAttributeForNonNullableReferenceTypes in Program.cs:

builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

Proprietà non nullable su tipi generici e attributo [Obbligatorio]

Le proprietà non nullable nei tipi generici devono includere l'attributo [Required] quando il tipo è obbligatorio. Nel codice TestRequired seguente non è necessario:

public class WeatherForecast<T>
{
    public string TestRequired { get; set; } = null!;
    public T? Inner { get; set; }
}

Nel codice seguente viene TestRequired contrassegnato in modo esplicito come richiesto:

using System.ComponentModel.DataAnnotations;

public class WeatherForecast<T>
{
    [Required]
    public string TestRequired { get; set; } = null!;
    public T? Inner { get; set; }
}

Convalida dell'attributo [Required] nel server

Nel server un valore obbligatorio viene considerato mancante se la proprietà è Null. Un campo non nullable è sempre valido e il [Required] messaggio di errore dell'attributo non viene mai visualizzato.

Può però succedere che l'associazione di modelli per una proprietà non nullable abbia esito negativo, generando un messaggio di errore, ad esempio The value '' is invalid. Per specificare un messaggio di errore personalizzato per la convalida lato server di tipi non nullable, sono disponibili le opzioni seguenti:

  • Il campo deve ammettere i valori Null, ad esempio decimal? anziché decimal. I tipi valore T> nullable<vengono considerati come tipi nullable standard.

  • Specificare il messaggio di errore predefinito che l'associazione di modelli deve usare, come illustrato nell'esempio seguente:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

    Per altre informazioni sugli errori dell'associazione di modelli che possono essere impostati come messaggi predefiniti, vedere DefaultModelBindingMessageProvider.

Convalida dell'attributo [Required] nel client

Le stringhe e i tipi non nullable sono gestiti in modo diverso nel client rispetto a come vengono gestiti nel server. Nel client:

  • Un valore viene considerato presente solo viene immesso un input per tale valore. La convalida lato client gestisce quindi i tipi non nullable come i tipi nullable.
  • Lo spazio vuoto in un campo stringa viene considerato un input valido per il metodo required della convalida di jQuery. La convalida lato server considera un campo stringa obbligatorio non valido solo se viene immesso uno spazio vuoto.

Come indicato in precedenza, i tipi non nullable vengono trattati come se avessero un attributo [Required(AllowEmptyStrings = true)]. Quindi viene eseguita la convalida lato client anche se non si applica l'attributo [Required(AllowEmptyStrings = true)]. Se invece non si usa l'attributo, viene visualizzato un messaggio di errore predefinito. Per specificare un messaggio di errore personalizzato, usare l'attributo.

Attributo [Remote]

L'attributo [Remote] implementa la convalida lato client che richiede la chiamata di un metodo nel server per determinare se l'input del campo è valido. Ad esempio, l'app potrebbe dover verificare se un nome utente è già in uso.

Per implementare la convalida remota:

  1. Creare un metodo di azione per JavaScript da chiamare. Il metodo remoto jQuery Validation prevede una JSrisposta ON:

    • true indica che i dati di input sono validi.
    • false, undefined o null indica che l'input non è valido. Visualizzare il messaggio di errore predefinito.
    • Qualsiasi altra stringa indica che l'input non è valido. Visualizzare la stringa come messaggio di errore personalizzato.

    Di seguito è riportato un esempio di un metodo di azione che restituisce un messaggio di errore personalizzato:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. Nella classe del modello annotare la proprietà con un attributo [Remote] che punta al metodo di azione di convalida, come illustrato nell'esempio seguente:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; } = null!;
    

Campi aggiuntivi

La proprietà [Remote] dell'attributo AdditionalFields consente di convalidare combinazioni di campi rispetto ai dati nel server. Ad esempio, se il modello User avesse le proprietà FirstName e LastName, potrebbe essere necessario controllare che non siano già esistenti utenti con la stessa coppia di nomi. Nell'esempio riportato di seguito viene illustrato come usare AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; } = null!;

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; } = null!;

AdditionalFieldspotrebbe essere impostato in modo esplicito sulle stringhe "FirstName" e "LastName", ma l'uso dell'operatore nameof semplifica il refactoring successivo. Il metodo di azione per questa convalida deve accettare entrambi firstName gli argomenti e lastName :

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userService.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

Quando si immette un nome o un cognome, JavaScript esegue una chiamata remota per verificare se tale coppia di nomi è in uso.

Per convalidare due o più campi aggiuntivi, specificarli sotto forma di elenco delimitato da virgole. Ad esempio, per aggiungere una proprietà MiddleName al modello, impostare l'attributo [Remote] come illustrato nell'esempio seguente:

[Remote(action: "VerifyName", controller: "Users",
    AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields, come tutti gli argomenti dell'attributo, deve essere un'espressione costante. Non usare quindi una stringa interpolata oppure chiamare Join per inizializzare AdditionalFields.

Alternative agli attributi predefiniti

Se si vuole usare un tipo di convalida non definita da attributi predefiniti, è possibile eseguire le operazioni seguenti:

Attributi personalizzati

Per gli scenari non gestiti dagli attributi di convalida predefiniti, è possibile creare attributi di convalida personalizzati. Creare una classe che eredita da ValidationAttribute ed eseguire l'override del metodo IsValid.

Il metodo IsValid accetta un oggetto denominato value, ovvero l'input da convalidare. Un overload accetta anche un oggetto ValidationContext, che contiene informazioni aggiuntive, ad esempio l'istanza del modello creato dall'associazione di modelli.

Nell'esempio seguente si convalida che la data di uscita di un film di genere Classic non sia successiva a un anno specifico. Attributo [ClassicMovie] :

  • Viene eseguito solo nel server.
  • Per i film classici, convalida la data di rilascio:
public class ClassicMovieAttribute : ValidationAttribute
{
    public ClassicMovieAttribute(int year)
        => Year = year;

    public int Year { get; }

    public string GetErrorMessage() =>
        $"Classic movies must have a release year no later than {Year}.";

    protected override ValidationResult? IsValid(
        object? value, ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value!).Year;

        if (movie.Genre == Genre.Classic && releaseYear > Year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }
}

La variabile movie nell'esempio precedente rappresenta un oggetto Movie contenente i dati dell'invio del modulo. Se la convalida ha esito negativo, viene restituito un codice ValidationResult con un messaggio di errore.

IValidatableObject

L'esempio precedente usa solo tipi Movie. Un'altra opzione per la convalida a livello di classe consiste nell'implementare IValidatableObject nella classe del modello, come illustrato nell'esempio seguente:

public class ValidatableMovie : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; } = null!;

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year no later than {_classicYear}.",
                new[] { nameof(ReleaseDate) });
        }
    }
}

Convalida del nodo di primo livello

I nodi di primo livello includono:

  • Parametri azione
  • Proprietà del controller
  • Parametri del gestore di pagina
  • Proprietà del modello di pagina

Vengono convalidati i nodi di primo livello associati al modello, oltre a convalidare le proprietà del modello. Nell'esempio seguente dell'app di esempio il VerifyPhone metodo usa per RegularExpressionAttribute convalidare il phone parametro action:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

I nodi di primo livello possono usare BindRequiredAttribute con gli attributi di convalida. Nell'esempio seguente dell'app di esempio, il CheckAge metodo specifica che il age parametro deve essere associato dalla stringa di query quando viene inviato il modulo:

[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{

Nella pagina Check Age (CheckAge.cshtml) sono disponibili due moduli. Il primo modulo invia un Age valore di come parametro della stringa di 99 query: https://localhost:5001/Users/CheckAge?Age=99.

Quando viene inviato un parametro correttamente formattato age dalla stringa di query, il modulo viene convalidato.

Il secondo modulo nella pagina Check Age invia il valore Age nel corpo della richiesta e la convalida ha esito negativo. L'associazione non riesce perché il parametro age deve provenire da una stringa di query.

Numero massimo di errori

La convalida si interrompe quando viene raggiunto il numero di errori (200 per impostazione predefinita). È possibile configurare questo numero con il codice seguente in Program.cs:

builder.Services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

builder.Services.AddSingleton
    <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();

Numero massimo di ricorsioni

ValidationVisitor attraversare l'oggetto grafico del modello che deve essere convalidato. Per i modelli profondi o ricorsivi infinitamente, la convalida può comportare un overflow dello stack. MvcOptions.MaxValidationDepth consente di interrompere la convalida in anticipo se la ricorsione del visitatore supera una profondità configurata. Il valore predefinito di MvcOptions.MaxValidationDepth è 32.

Corto circuito automatico

La convalida viene ignorata automaticamente (corto circuito) se l'oggetto grafico non richiede la convalida. Gli oggetti per cui viene ignorata la convalida sono le raccolte di primitive (ad esempio byte[], string[], Dictionary<string, string>) e gli oggetti grafici complessi che non hanno validator.

Convalida lato client

La convalida lato client non consente l'invio del modulo finché non è ritenuto valido. Tramite il pulsante Invia viene eseguito JavaScript, che invia il modulo oppure visualizza i messaggi di errore.

La convalida lato client evitare un inutile round trip al server in caso di errori di input in un modulo. I riferimenti di script seguenti in _Layout.cshtml e _ValidationScriptsPartial.cshtml supportano la convalida lato client:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.3/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.12/jquery.validate.unobtrusive.js"></script>

Lo script jQuery Unobtrusive Validation è una libreria front-end Microsoft personalizzata basata sul popolare plug-in jQuery Validation . Senza Query Unobtrusive Validation, sarebbe necessario scrivere il codice della stessa logica di convalida in due posizioni, vale a dire negli attributi di convalida lato server nelle proprietà del modello e nuovamente negli script lato client. Invece, gli helper tage agli helper HTML usano gli attributi di convalida e i metadati di tipo delle proprietà del modello per eseguire il rendering degli attributi data- HTML 5 negli elementi del modulo che devono essere convalidati. jQuery Unobtrusive Validation analizza gli data- attributi e passa la logica alla convalida jQuery, "copiando" in modo efficace la logica di convalida lato server nel client. È possibile visualizzare gli errori di convalida nel client tramite gli helper tag, come illustrato di seguito:

<div class="form-group">
    <label asp-for="Movie.ReleaseDate" class="control-label"></label>
    <input asp-for="Movie.ReleaseDate" class="form-control" />
    <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>

Gli helper tag precedenti eseguono il rendering del codice HTML seguente:

<div class="form-group">
    <label class="control-label" for="Movie_ReleaseDate">Release Date</label>
    <input class="form-control" type="date" data-val="true"
        data-val-required="The Release Date field is required."
        id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
    <span class="text-danger field-validation-valid"
        data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>

Si noti che gli attributi data- nell'output HTML corrispondono agli attributi di convalida per la proprietà Movie.ReleaseDate. L'attributo data-val-required contiene un messaggio di errore che viene visualizzato se l'utente non compila il campo relativo alla data di uscita. JQuery Unobtrusive Validation passa questo valore al metodo jQuery Validation required(), che quindi visualizza il messaggio nell'elemento span> a <cui si accompagna.

La convalida dei tipi di dati è basata sul tipo .NET di una proprietà, a meno che non venga sottoposto a override da un attributo [DataType]. I browser generano messaggi di errore predefiniti propri che il pacchetto jQuery Validation Unobtrusive Validation può comunque sostituire. [DataType] attributi e sottoclassi, ad esempio [EmailAddress] consentono di specificare il messaggio di errore.

Convalida non invasiva

Per informazioni sulla convalida non invasiva, vedere questo problema di GitHub.

Aggiungere la convalida a moduli dinamici

JQuery Unobtrusive Validation passa la logica di convalida e i parametri alla convalida jQuery al primo caricamento della pagina. La convalida non viene quindi eseguita automaticamente nei moduli generati in modo dinamico. Per abilitare la convalida, chiedere a jQuery Unobtrusive Validation di analizzare il modulo dinamico immediatamente dopo essere stato creato. Il codice riportato di seguito configura ad esempio la convalida lato client in un modulo che è stato aggiunto tramite AJAX.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Il metodo $.validator.unobtrusive.parse() accetta un selettore jQuery per ogni argomento. Questo metodo indica a jQuery Unobtrusive Validation di analizzare gli attributi data- dei moduli all'interno di tale tipo di selettore. I valori di questi attributi vengono quindi passati al plug-in jQuery Validation.

Aggiungere la convalida a controlli dinamici

Il metodo $.validator.unobtrusive.parse() funziona su un intero modulo e non sui singoli controlli generati dinamicamente, ad esempio <input> e <select/>. Per eseguire il reparse del modulo, rimuovere i dati di convalida aggiunti quando il modulo è stato precedentemente analizzato, come illustrato nell'esempio seguente:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validation
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Convalida lato client personalizzata

La convalida lato client personalizzata viene eseguita generando data- attributi HTML che funzionano con un adattatore di convalida jQuery personalizzato. Il codice dell'adapter di esempio seguente è stato scritto per gli attributi [ClassicMovie] e [ClassicMovieWithClientValidator] presentati precedentemente nell'articolo:

$.validator.addMethod('classicmovie', function (value, element, params) {
    var genre = $(params[0]).val(), year = params[1], date = new Date(value);

    // The Classic genre has a value of '0'.
    if (genre && genre.length > 0 && genre[0] === '0') {
        // The release date for a Classic is valid if it's no greater than the given year.
        return date.getUTCFullYear() <= year;
    }

    return true;
});

$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
    var element = $(options.form).find('select#Movie_Genre')[0];

    options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
    options.messages['classicmovie'] = options.message;
});

Per informazioni su come scrivere adapter, vedere la documentazione relativa alla convalida di jQuery.

L'uso di un adapter per un determinato campo viene attivato dagli attributi data-, i quali eseguono le operazioni seguenti:

  • Contrassegnano il campo come campo da sottoporre a convalida (data-val="true").
  • Identificano un nome della regola di convalida e un testo del messaggio di errore, ad esempio data-val-rulename="Error message.".
  • Specificare i parametri aggiuntivi necessari al validator, ad esempio data-val-rulename-param1="value".

L'esempio seguente mostra gli data- attributi per l'attributo dell'app diClassicMovie esempio:

<input class="form-control" type="date"
    data-val="true"
    data-val-classicmovie="Classic movies must have a release year no later than 1960."
    data-val-classicmovie-year="1960"
    data-val-required="The Release Date field is required."
    id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">

Come già accennato in precedenza, gli helper tag e gli helper HTML usano le informazioni degli attributi di convalida per eseguire il rendering degli attributi data-. Per scrivere il codice che crea attributi HTML data- personalizzati, sono disponibili due opzioni:

  • Creare una classe che deriva da AttributeAdapterBase<TAttribute> e una classe che implementa IValidationAttributeAdapterProvider e registrare l'attributo e il relativo adapter nell'inserimento delle dipendenze. Questo metodo segue il principio di responsabilità singola in quanto il codice di convalida correlato al server e correlato al client si trova in classi separate. L'adattatore ha anche il vantaggio che, dal momento che è registrato in inserimento di dipendenze, altri servizi in di inserimento di dipendenze sono disponibili, se necessario.
  • Implementare IClientModelValidator nella classe ValidationAttribute. Questo metodo potrebbe essere appropriato se l'attributo non esegue la convalida lato server e non richiede altri servizi dell'inserimento delle dipendenze.

AttributeAdapter per la convalida lato client

Questo metodo di rendering data- degli attributi in HTML viene usato dall'attributo nell'appClassicMoviedi esempio. Per aggiungere la convalida lato client usando questo metodo:

  1. Creare una classe di adapter dell'attributo per l'attributo di convalida personalizzata. Derivare la classe da AttributeAdapterBase<TAttribute>. Creare un metodo AddValidation che aggiunge gli attributi data- all'output sottoposto a rendering, come illustrato in questo esempio:

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        public ClassicMovieAttributeAdapter(
            ClassicMovieAttribute attribute, IStringLocalizer? stringLocalizer)
            : base(attribute, stringLocalizer)
        {
    
        }
    
        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public override string GetErrorMessage(ModelValidationContextBase validationContext)
            => Attribute.GetErrorMessage();
    }
    
  2. Creare una classe di provider dell'adapter che implementa IValidationAttributeAdapterProvider. Nel metodo GetAttributeAdapter passare l'attributo personalizzato al costruttore dell'adapter, come illustrato in questo esempio:

    public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter? GetAttributeAdapter(
            ValidationAttribute attribute, IStringLocalizer? stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
    
            return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    
  3. Registrare il provider dell'adapter per l'inserimento delle dipendenze in Program.cs:

    builder.Services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    builder.Services.AddSingleton
        <IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator per la convalida lato client

Questo metodo di rendering data- degli attributi in HTML viene usato dall'attributo nell'appClassicMovieWithClientValidatordi esempio. Per aggiungere la convalida lato client usando questo metodo:

  • Nell'attributo di convalida personalizzato implementare l'interfaccia IClientModelValidator e creare un metodo AddValidation. Nel metodo AddValidation aggiungere gli attributi data- per la convalida, come illustrato nell'esempio seguente:

    public class ClassicMovieWithClientValidatorAttribute :
        ValidationAttribute, IClientModelValidator
    {
        public ClassicMovieWithClientValidatorAttribute(int year)
            => Year = year;
    
        public int Year { get; }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public string GetErrorMessage() =>
            $"Classic movies must have a release year no later than {Year}.";
    
        protected override ValidationResult? IsValid(
            object? value, ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value!).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > Year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
    }
    

Disabilitare la convalida lato client

Il codice seguente disabilita la convalida client in Razor Pages:

builder.Services.AddRazorPages()
    .AddViewOptions(options =>
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    });

Altre opzioni per disabilitare la convalida lato client:

  • Impostare come commento il riferimento a _ValidationScriptsPartial in tutti i .cshtml file.
  • Rimuovere il contenuto del file Pages\Shared_ValidationScriptsPartial.cshtml .

L'approccio precedente non impedisce la convalida lato client di ASP.NET libreria di classi core IdentityRazor . Per altre informazioni, vedere Scaffolding Identity nei progetti ASP.NET Core.

Risorse aggiuntive

Questo articolo illustra come convalidare l'input dell'utente in un'app MVC o Razor Pages core ASP.NET.

Visualizzare o scaricare il codice di esempio (procedura per il download).

Stato del modello

Lo stato del modello rappresenta gli errori che provengono da due sottosistemi: associazione di modelli e convalida del modello. Gli errori originati dall'associazione di modelli sono in genere errori di conversione dei dati. Ad esempio, un valore "x" viene immesso in un campo integer. La convalida del modello viene eseguita dopo l'associazione di modelli e segnala gli errori in cui i dati non sono conformi alle regole business. Ad esempio, un valore 0 viene immesso in un campo che prevede una classificazione compresa tra 1 e 5.

Sia l'associazione del modello che la convalida del modello vengono eseguite prima dell'esecuzione di un'azione del controller o di un Razor metodo del gestore Pages. Per le app Web, è responsabilità dell'app esaminare ModelState.IsValid e rispondere nel modo appropriato. Le app Web in genere visualizzare di nuovo la pagina con un messaggio di errore:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

I controller API Web non devono verificare ModelState.IsValid se hanno l'attributo [ApiController]. In tal caso, viene restituita una risposta HTTP 400 automatica contenente i dettagli dell'errore quando lo stato del modello non è valido. Per altre informazioni, vedere Risposte HTTP 400 automatiche.

Rieseguire la convalida

La convalida è automatica, ma potrebbe essere necessario ripeterla manualmente. Ad esempio, è possibile che si calcoli un valore per una proprietà e che si voglia rieseguire la convalida dopo aver impostato la proprietà sul valore calcolato. Per rieseguire la convalida, chiamare ModelStateDictionary.ClearValidationState per cancellare la convalida specifica del modello da convalidare seguito da TryValidateModel:

public async Task<IActionResult> OnPostTryValidateAsync()
{
    var modifiedReleaseDate = DateTime.Now.Date;
    Movie.ReleaseDate = modifiedReleaseDate;

    ModelState.ClearValidationState(nameof(Movie));
    if (!TryValidateModel(Movie, nameof(Movie)))
    {
        return Page();
    }

    _context.Movies.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Attributi di convalida

Gli attributi di convalida consentono di specificare le regole di convalida per le proprietà del modello. Nell'esempio seguente dell'app di esempio viene illustrata una classe di modello annotata con attributi di convalida. L'attributo [ClassicMovie] è un attributo di convalida personalizzato e gli altri sono incorporati. Non visualizzato è [ClassicMovieWithClientValidator], che mostra un modo alternativo per implementare un attributo personalizzato.

public class Movie
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [ClassicMovie(1960)]
    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }
}

Attributi predefiniti

Di seguito sono elencati alcuni degli attributi di convalida predefiniti:

  • [ValidateNever]: indica che una proprietà o un parametro deve essere escluso dalla convalida.
  • [CreditCard]: convalida che la proprietà abbia un formato di carta di credito. Richiede metodi aggiuntivi per la convalida di jQuery.
  • [Confronto]: convalida che due proprietà in un modello corrispondano.
  • [EmailAddress]: convalida che la proprietà abbia un formato di posta elettronica.
  • [Telefono]: convalida che la proprietà abbia un formato di numero di telefono.
  • [Intervallo]: convalida che il valore della proprietà rientra in un intervallo specificato.
  • [RegularExpression]: convalida che il valore della proprietà corrisponda a un'espressione regolare specificata.
  • [Obbligatorio]: convalida che il campo non sia Null. Per informazioni dettagliate sul comportamento di questo attributo, vedere [Required] attributo .
  • [StringLength]: verifica che un valore della proprietà stringa non superi un limite di lunghezza specificato.
  • [Url]: convalida che la proprietà abbia un formato URL.
  • [Remoto]: convalida l'input nel client chiamando un metodo di azione nel server. Per informazioni dettagliate sul comportamento di questo attributo, vedere [Remote] attributo .

Un elenco completo degli attributi di convalida è disponibile nello spazio dei System.ComponentModel.DataAnnotations nomi .

messaggi di errore

Gli attributi di convalida consentono di specificare il messaggio di errore da visualizzare in caso di input non valido. Ad esempio:

[StringLength(8, ErrorMessage = "Name length can't be more than 8.")]

Internamente gli attributi chiamano String.Format con un segnaposto per il nome campo e talvolta con segnaposto aggiuntivi. Ad esempio:

[StringLength(8, ErrorMessage = "{0} length must be between {2} and {1}.", MinimumLength = 6)]

Quando applicato a una proprietà Name, il messaggio di errore creato con il codice precedente sarà "La lunghezza del nome utente deve essere compresa tra 6 e 8".

Per scoprire i parametri passati a String.Format per un messaggio di errore dell'attributo specifico, vedere il codice sorgente DataAnnotations.

Tipi di riferimento non nullable e attributo [Obbligatorio]

Il sistema di convalida considera parametri non nullable o proprietà associate come se avessero un [Required(AllowEmptyStrings = true)] attributo. Abilitando Nullablei contesti, MVC inizia implicitamente a convalidare proprietà o parametri non nullable come se fossero stati attribuiti con l'attributo [Required(AllowEmptyStrings = true)] . Osservare il codice seguente:

public class Person
{
    public string Name { get; set; }
}

Se l'app è stata compilata con <Nullable>enable</Nullable>, un valore mancante per Name in un JSpost ON o form genera un errore di convalida. Usare un tipo riferimento nullable per consentire l'impostazione di valori Null o mancanti per la Name proprietà :

public class Person
{
    public string? Name { get; set; }
}

Questo comportamento può essere disabilitato configurando SuppressImplicitRequiredAttributeForNonNullableReferenceTypes in Startup.ConfigureServices:

services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

Convalida dell'attributo [Required] nel server

Nel server un valore obbligatorio viene considerato mancante se la proprietà è Null. Un campo non nullable è sempre valido e il [Required] messaggio di errore dell'attributo non viene mai visualizzato.

Può però succedere che l'associazione di modelli per una proprietà non nullable abbia esito negativo, generando un messaggio di errore, ad esempio The value '' is invalid. Per specificare un messaggio di errore personalizzato per la convalida lato server di tipi non nullable, sono disponibili le opzioni seguenti:

  • Il campo deve ammettere i valori Null, ad esempio decimal? anziché decimal. I tipi valore T> nullable<vengono considerati come tipi nullable standard.

  • Specificare il messaggio di errore predefinito che l'associazione di modelli deve usare, come illustrato nell'esempio seguente:

    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    services.AddSingleton<IValidationAttributeAdapterProvider,
        CustomValidationAttributeAdapterProvider>();
    

    Per altre informazioni sugli errori dell'associazione di modelli che possono essere impostati come messaggi predefiniti, vedere DefaultModelBindingMessageProvider.

Convalida dell'attributo [Required] nel client

Le stringhe e i tipi non nullable sono gestiti in modo diverso nel client rispetto a come vengono gestiti nel server. Nel client:

  • Un valore viene considerato presente solo viene immesso un input per tale valore. La convalida lato client gestisce quindi i tipi non nullable come i tipi nullable.
  • Lo spazio vuoto in un campo stringa viene considerato un input valido per il metodo required della convalida di jQuery. La convalida lato server considera un campo stringa obbligatorio non valido solo se viene immesso uno spazio vuoto.

Come indicato in precedenza, i tipi non nullable vengono trattati come se avessero un attributo [Required(AllowEmptyStrings = true)]. Quindi viene eseguita la convalida lato client anche se non si applica l'attributo [Required(AllowEmptyStrings = true)]. Se invece non si usa l'attributo, viene visualizzato un messaggio di errore predefinito. Per specificare un messaggio di errore personalizzato, usare l'attributo.

Attributo [Remote]

L'attributo [Remote] implementa la convalida lato client che richiede la chiamata di un metodo nel server per determinare se l'input del campo è valido. Ad esempio, l'app potrebbe dover verificare se un nome utente è già in uso.

Per implementare la convalida remota:

  1. Creare un metodo di azione per JavaScript da chiamare. Il metodo remoto jQuery Validation prevede una JSrisposta ON:

    • true indica che i dati di input sono validi.
    • false, undefined o null indica che l'input non è valido. Visualizzare il messaggio di errore predefinito.
    • Qualsiasi altra stringa indica che l'input non è valido. Visualizzare la stringa come messaggio di errore personalizzato.

    Di seguito è riportato un esempio di un metodo di azione che restituisce un messaggio di errore personalizzato:

    [AcceptVerbs("GET", "POST")]
    public IActionResult VerifyEmail(string email)
    {
        if (!_userService.VerifyEmail(email))
        {
            return Json($"Email {email} is already in use.");
        }
    
        return Json(true);
    }
    
  2. Nella classe del modello annotare la proprietà con un attributo [Remote] che punta al metodo di azione di convalida, come illustrato nell'esempio seguente:

    [Remote(action: "VerifyEmail", controller: "Users")]
    public string Email { get; set; }
    

Campi aggiuntivi

La proprietà [Remote] dell'attributo AdditionalFields consente di convalidare combinazioni di campi rispetto ai dati nel server. Ad esempio, se il modello User avesse le proprietà FirstName e LastName, potrebbe essere necessario controllare che non siano già esistenti utenti con la stessa coppia di nomi. Nell'esempio riportato di seguito viene illustrato come usare AdditionalFields:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(LastName))]
[Display(Name = "First Name")]
public string FirstName { get; set; }

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName))]
[Display(Name = "Last Name")]
public string LastName { get; set; }

AdditionalFieldspotrebbe essere impostato in modo esplicito sulle stringhe "FirstName" e "LastName", ma l'uso dell'operatore nameof semplifica il refactoring successivo. Il metodo di azione per questa convalida deve accettare entrambi firstName gli argomenti e lastName :

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyName(string firstName, string lastName)
{
    if (!_userService.VerifyName(firstName, lastName))
    {
        return Json($"A user named {firstName} {lastName} already exists.");
    }

    return Json(true);
}

Quando si immette un nome o un cognome, JavaScript esegue una chiamata remota per verificare se tale coppia di nomi è in uso.

Per convalidare due o più campi aggiuntivi, specificarli sotto forma di elenco delimitato da virgole. Ad esempio, per aggiungere una proprietà MiddleName al modello, impostare l'attributo [Remote] come illustrato nell'esempio seguente:

[Remote(action: "VerifyName", controller: "Users", AdditionalFields = nameof(FirstName) + "," + nameof(LastName))]
public string MiddleName { get; set; }

AdditionalFields, come tutti gli argomenti dell'attributo, deve essere un'espressione costante. Non usare quindi una stringa interpolata oppure chiamare Join per inizializzare AdditionalFields.

Alternative agli attributi predefiniti

Se si vuole usare un tipo di convalida non definita da attributi predefiniti, è possibile eseguire le operazioni seguenti:

Attributi personalizzati

Per gli scenari non gestiti dagli attributi di convalida predefiniti, è possibile creare attributi di convalida personalizzati. Creare una classe che eredita da ValidationAttribute ed eseguire l'override del metodo IsValid.

Il metodo IsValid accetta un oggetto denominato value, ovvero l'input da convalidare. Un overload accetta anche un oggetto ValidationContext, che contiene informazioni aggiuntive, ad esempio l'istanza del modello creato dall'associazione di modelli.

Nell'esempio seguente si convalida che la data di uscita di un film di genere Classic non sia successiva a un anno specifico. Attributo [ClassicMovie] :

  • Viene eseguito solo nel server.
  • Per i film classici, convalida la data di rilascio:
public class ClassicMovieAttribute : ValidationAttribute
{
    public ClassicMovieAttribute(int year)
    {
        Year = year;
    }

    public int Year { get; }

    public string GetErrorMessage() =>
        $"Classic movies must have a release year no later than {Year}.";

    protected override ValidationResult IsValid(object value,
        ValidationContext validationContext)
    {
        var movie = (Movie)validationContext.ObjectInstance;
        var releaseYear = ((DateTime)value).Year;

        if (movie.Genre == Genre.Classic && releaseYear > Year)
        {
            return new ValidationResult(GetErrorMessage());
        }

        return ValidationResult.Success;
    }
}

La variabile movie nell'esempio precedente rappresenta un oggetto Movie contenente i dati dell'invio del modulo. Se la convalida ha esito negativo, viene restituito un codice ValidationResult con un messaggio di errore.

IValidatableObject

L'esempio precedente usa solo tipi Movie. Un'altra opzione per la convalida a livello di classe consiste nell'implementare IValidatableObject nella classe del modello, come illustrato nell'esempio seguente:

public class ValidatableMovie : IValidatableObject
{
    private const int _classicYear = 1960;

    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Title { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; set; }

    [Required]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }

    public Genre Genre { get; set; }

    public bool Preorder { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Genre == Genre.Classic && ReleaseDate.Year > _classicYear)
        {
            yield return new ValidationResult(
                $"Classic movies must have a release year no later than {_classicYear}.",
                new[] { nameof(ReleaseDate) });
        }
    }
}

Convalida del nodo di primo livello

I nodi di primo livello includono:

  • Parametri azione
  • Proprietà del controller
  • Parametri del gestore di pagina
  • Proprietà del modello di pagina

Vengono convalidati i nodi di primo livello associati al modello, oltre a convalidare le proprietà del modello. Nell'esempio seguente dell'app di esempio il VerifyPhone metodo usa per RegularExpressionAttribute convalidare il phone parametro action:

[AcceptVerbs("GET", "POST")]
public IActionResult VerifyPhone(
    [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)
{
    if (!ModelState.IsValid)
    {
        return Json($"Phone {phone} has an invalid format. Format: ###-###-####");
    }

    return Json(true);
}

I nodi di primo livello possono usare BindRequiredAttribute con gli attributi di convalida. Nell'esempio seguente dell'app di esempio, il CheckAge metodo specifica che il age parametro deve essere associato dalla stringa di query quando viene inviato il modulo:

[HttpPost]
public IActionResult CheckAge([BindRequired, FromQuery] int age)
{

Nella pagina Check Age (CheckAge.cshtml) sono disponibili due moduli. Il primo modulo invia un Age valore di come parametro della stringa di 99 query: https://localhost:5001/Users/CheckAge?Age=99.

Quando viene inviato un parametro correttamente formattato age dalla stringa di query, il modulo viene convalidato.

Il secondo modulo nella pagina Check Age invia il valore Age nel corpo della richiesta e la convalida ha esito negativo. L'associazione non riesce perché il parametro age deve provenire da una stringa di query.

Numero massimo di errori

La convalida si interrompe quando viene raggiunto il numero di errori (200 per impostazione predefinita). È possibile configurare questo numero con il codice seguente in Startup.ConfigureServices:

services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

services.AddSingleton<IValidationAttributeAdapterProvider,
    CustomValidationAttributeAdapterProvider>();

Numero massimo di ricorsioni

ValidationVisitor attraversare l'oggetto grafico del modello che deve essere convalidato. Per i modelli profondi o ricorsivi infinitamente, la convalida può comportare un overflow dello stack. MvcOptions.MaxValidationDepth consente di interrompere la convalida in anticipo se la ricorsione del visitatore supera una profondità configurata. Il valore predefinito di MvcOptions.MaxValidationDepth è 32.

Corto circuito automatico

La convalida viene ignorata automaticamente (corto circuito) se l'oggetto grafico non richiede la convalida. Gli oggetti per cui viene ignorata la convalida sono le raccolte di primitive (ad esempio byte[], string[], Dictionary<string, string>) e gli oggetti grafici complessi che non hanno validator.

Convalida lato client

La convalida lato client non consente l'invio del modulo finché non è ritenuto valido. Tramite il pulsante Invia viene eseguito JavaScript, che invia il modulo oppure visualizza i messaggi di errore.

La convalida lato client evitare un inutile round trip al server in caso di errori di input in un modulo. I riferimenti di script seguenti in _Layout.cshtml e _ValidationScriptsPartial.cshtml supportano la convalida lato client:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.js"></script>

Lo script jQuery Unobtrusive Validation è una libreria front-end Microsoft personalizzata basata sul popolare plug-in jQuery Validation . Senza Query Unobtrusive Validation, sarebbe necessario scrivere il codice della stessa logica di convalida in due posizioni, vale a dire negli attributi di convalida lato server nelle proprietà del modello e nuovamente negli script lato client. Invece, gli helper tage agli helper HTML usano gli attributi di convalida e i metadati di tipo delle proprietà del modello per eseguire il rendering degli attributi data- HTML 5 negli elementi del modulo che devono essere convalidati. jQuery Unobtrusive Validation analizza gli data- attributi e passa la logica alla convalida jQuery, "copiando" in modo efficace la logica di convalida lato server nel client. È possibile visualizzare gli errori di convalida nel client tramite gli helper tag, come illustrato di seguito:

<div class="form-group">
    <label asp-for="Movie.ReleaseDate" class="control-label"></label>
    <input asp-for="Movie.ReleaseDate" class="form-control" />
    <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>

Gli helper tag precedenti eseguono il rendering del codice HTML seguente:

<div class="form-group">
    <label class="control-label" for="Movie_ReleaseDate">Release Date</label>
    <input class="form-control" type="date" data-val="true"
        data-val-required="The Release Date field is required."
        id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">
    <span class="text-danger field-validation-valid"
        data-valmsg-for="Movie.ReleaseDate" data-valmsg-replace="true"></span>
</div>

Si noti che gli attributi data- nell'output HTML corrispondono agli attributi di convalida per la proprietà Movie.ReleaseDate. L'attributo data-val-required contiene un messaggio di errore che viene visualizzato se l'utente non compila il campo relativo alla data di uscita. JQuery Unobtrusive Validation passa questo valore al metodo jQuery Validation required(), che quindi visualizza il messaggio nell'elemento span> a <cui si accompagna.

La convalida dei tipi di dati è basata sul tipo .NET di una proprietà, a meno che non venga sottoposto a override da un attributo [DataType]. I browser generano messaggi di errore predefiniti propri che il pacchetto jQuery Validation Unobtrusive Validation può comunque sostituire. [DataType] attributi e sottoclassi, ad esempio [EmailAddress] consentono di specificare il messaggio di errore.

Convalida non invasiva

Per informazioni sulla convalida non invasiva, vedere questo problema di GitHub.

Aggiungere la convalida a moduli dinamici

JQuery Unobtrusive Validation passa la logica di convalida e i parametri alla convalida jQuery al primo caricamento della pagina. La convalida non viene quindi eseguita automaticamente nei moduli generati in modo dinamico. Per abilitare la convalida, chiedere a jQuery Unobtrusive Validation di analizzare il modulo dinamico immediatamente dopo essere stato creato. Il codice riportato di seguito configura ad esempio la convalida lato client in un modulo che è stato aggiunto tramite AJAX.

$.get({
    url: "https://url/that/returns/a/form",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add form. " + errorThrown);
    },
    success: function(newFormHTML) {
        var container = document.getElementById("form-container");
        container.insertAdjacentHTML("beforeend", newFormHTML);
        var forms = container.getElementsByTagName("form");
        var newForm = forms[forms.length - 1];
        $.validator.unobtrusive.parse(newForm);
    }
})

Il metodo $.validator.unobtrusive.parse() accetta un selettore jQuery per ogni argomento. Questo metodo indica a jQuery Unobtrusive Validation di analizzare gli attributi data- dei moduli all'interno di tale tipo di selettore. I valori di questi attributi vengono quindi passati al plug-in jQuery Validation.

Aggiungere la convalida a controlli dinamici

Il metodo $.validator.unobtrusive.parse() funziona su un intero modulo e non sui singoli controlli generati dinamicamente, ad esempio <input> e <select/>. Per eseguire il reparse del modulo, rimuovere i dati di convalida aggiunti quando il modulo è stato precedentemente analizzato, come illustrato nell'esempio seguente:

$.get({
    url: "https://url/that/returns/a/control",
    dataType: "html",
    error: function(jqXHR, textStatus, errorThrown) {
        alert(textStatus + ": Couldn't add control. " + errorThrown);
    },
    success: function(newInputHTML) {
        var form = document.getElementById("my-form");
        form.insertAdjacentHTML("beforeend", newInputHTML);
        $(form).removeData("validator")    // Added by jQuery Validation
               .removeData("unobtrusiveValidation");   // Added by jQuery Unobtrusive Validation
        $.validator.unobtrusive.parse(form);
    }
})

Convalida lato client personalizzata

La convalida lato client personalizzata viene eseguita generando data- attributi HTML che funzionano con un adattatore di convalida jQuery personalizzato. Il codice dell'adapter di esempio seguente è stato scritto per gli attributi [ClassicMovie] e [ClassicMovieWithClientValidator] presentati precedentemente nell'articolo:

$.validator.addMethod('classicmovie', function (value, element, params) {
    var genre = $(params[0]).val(), year = params[1], date = new Date(value);

    // The Classic genre has a value of '0'.
    if (genre && genre.length > 0 && genre[0] === '0') {
        // The release date for a Classic is valid if it's no greater than the given year.
        return date.getUTCFullYear() <= year;
    }

    return true;
});

$.validator.unobtrusive.adapters.add('classicmovie', ['year'], function (options) {
    var element = $(options.form).find('select#Movie_Genre')[0];

    options.rules['classicmovie'] = [element, parseInt(options.params['year'])];
    options.messages['classicmovie'] = options.message;
});

Per informazioni su come scrivere adapter, vedere la documentazione relativa alla convalida di jQuery.

L'uso di un adapter per un determinato campo viene attivato dagli attributi data-, i quali eseguono le operazioni seguenti:

  • Contrassegnano il campo come campo da sottoporre a convalida (data-val="true").
  • Identificano un nome della regola di convalida e un testo del messaggio di errore, ad esempio data-val-rulename="Error message.".
  • Specificare i parametri aggiuntivi necessari al validator, ad esempio data-val-rulename-param1="value".

L'esempio seguente mostra gli data- attributi per l'attributo dell'app diClassicMovie esempio:

<input class="form-control" type="date"
    data-val="true"
    data-val-classicmovie="Classic movies must have a release year no later than 1960."
    data-val-classicmovie-year="1960"
    data-val-required="The Release Date field is required."
    id="Movie_ReleaseDate" name="Movie.ReleaseDate" value="">

Come già accennato in precedenza, gli helper tag e gli helper HTML usano le informazioni degli attributi di convalida per eseguire il rendering degli attributi data-. Per scrivere il codice che crea attributi HTML data- personalizzati, sono disponibili due opzioni:

  • Creare una classe che deriva da AttributeAdapterBase<TAttribute> e una classe che implementa IValidationAttributeAdapterProvider e registrare l'attributo e il relativo adapter nell'inserimento delle dipendenze. Questo metodo segue il principio di responsabilità singola in quanto il codice di convalida correlato al server e correlato al client si trova in classi separate. L'adattatore ha anche il vantaggio che, dal momento che è registrato in inserimento di dipendenze, altri servizi in di inserimento di dipendenze sono disponibili, se necessario.
  • Implementare IClientModelValidator nella classe ValidationAttribute. Questo metodo potrebbe essere appropriato se l'attributo non esegue la convalida lato server e non richiede altri servizi dell'inserimento delle dipendenze.

AttributeAdapter per la convalida lato client

Questo metodo di rendering data- degli attributi in HTML viene usato dall'attributo nell'appClassicMoviedi esempio. Per aggiungere la convalida lato client usando questo metodo:

  1. Creare una classe di adapter dell'attributo per l'attributo di convalida personalizzata. Derivare la classe da AttributeAdapterBase<TAttribute>. Creare un metodo AddValidation che aggiunge gli attributi data- all'output sottoposto a rendering, come illustrato in questo esempio:

    public class ClassicMovieAttributeAdapter : AttributeAdapterBase<ClassicMovieAttribute>
    {
        public ClassicMovieAttributeAdapter(ClassicMovieAttribute attribute,
            IStringLocalizer stringLocalizer)
            : base(attribute, stringLocalizer)
        {
    
        }
    
        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage(context));
    
            var year = Attribute.Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public override string GetErrorMessage(ModelValidationContextBase validationContext) =>
            Attribute.GetErrorMessage();
    }
    
  2. Creare una classe di provider dell'adapter che implementa IValidationAttributeAdapterProvider. Nel metodo GetAttributeAdapter passare l'attributo personalizzato al costruttore dell'adapter, come illustrato in questo esempio:

    public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider baseProvider =
            new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute,
            IStringLocalizer stringLocalizer)
        {
            if (attribute is ClassicMovieAttribute classicMovieAttribute)
            {
                return new ClassicMovieAttributeAdapter(classicMovieAttribute, stringLocalizer);
            }
    
            return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    
  3. Registrare il provider dell'adapter per l'inserimento delle dipendenze in Startup.ConfigureServices:

    services.AddRazorPages()
        .AddMvcOptions(options =>
        {
            options.MaxModelValidationErrors = 50;
            options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
                _ => "The field is required.");
        });
    
    services.AddSingleton<IValidationAttributeAdapterProvider,
        CustomValidationAttributeAdapterProvider>();
    

IClientModelValidator per la convalida lato client

Questo metodo di rendering data- degli attributi in HTML viene usato dall'attributo nell'appClassicMovieWithClientValidatordi esempio. Per aggiungere la convalida lato client usando questo metodo:

  • Nell'attributo di convalida personalizzato implementare l'interfaccia IClientModelValidator e creare un metodo AddValidation. Nel metodo AddValidation aggiungere gli attributi data- per la convalida, come illustrato nell'esempio seguente:

    public class ClassicMovieWithClientValidatorAttribute :
        ValidationAttribute, IClientModelValidator
    {
        public ClassicMovieWithClientValidatorAttribute(int year)
        {
            Year = year;
        }
    
        public int Year { get; }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
    
            var year = Year.ToString(CultureInfo.InvariantCulture);
            MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
        }
    
        public string GetErrorMessage() =>
            $"Classic movies must have a release year no later than {Year}.";
    
        protected override ValidationResult IsValid(object value,
            ValidationContext validationContext)
        {
            var movie = (Movie)validationContext.ObjectInstance;
            var releaseYear = ((DateTime)value).Year;
    
            if (movie.Genre == Genre.Classic && releaseYear > Year)
            {
                return new ValidationResult(GetErrorMessage());
            }
    
            return ValidationResult.Success;
        }
    
        private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
    
            attributes.Add(key, value);
            return true;
        }
    }
    

Disabilitare la convalida lato client

Il codice seguente disabilita la convalida client in Razor Pages:

services.AddRazorPages()
    .AddViewOptions(options =>
    {
        options.HtmlHelperOptions.ClientValidationEnabled = false;
    });

Altre opzioni per disabilitare la convalida lato client:

  • Impostare come commento il riferimento a _ValidationScriptsPartial in tutti i .cshtml file.
  • Rimuovere il contenuto del file Pages\Shared_ValidationScriptsPartial.cshtml .

L'approccio precedente non impedisce la convalida lato client di ASP.NET libreria di classi core IdentityRazor . Per altre informazioni, vedere Scaffolding Identity nei progetti ASP.NET Core.

Risorse aggiuntive